Overview

Namespaces

  • Charcoal
    • Loader
    • Model
      • Service
      • ServiceProvider
    • Source
      • Database
    • Validator

Classes

  • Charcoal\Loader\CollectionLoader
  • Charcoal\Loader\FileLoader
  • Charcoal\Model\AbstractMetadata
  • Charcoal\Model\AbstractModel
  • Charcoal\Model\Collection
  • Charcoal\Model\Model
  • Charcoal\Model\ModelMetadata
  • Charcoal\Model\ModelValidator
  • Charcoal\Model\Service\MetadataLoader
  • Charcoal\Model\Service\ModelBuilder
  • Charcoal\Model\Service\ModelLoader
  • Charcoal\Model\Service\ModelLoaderBuilder
  • Charcoal\Model\ServiceProvider\ModelServiceProvider
  • Charcoal\Source\AbstractSource
  • Charcoal\Source\Database\DatabaseFilter
  • Charcoal\Source\Database\DatabaseOrder
  • Charcoal\Source\Database\DatabasePagination
  • Charcoal\Source\DatabaseSource
  • Charcoal\Source\DatabaseSourceConfig
  • Charcoal\Source\Filter
  • Charcoal\Source\Order
  • Charcoal\Source\Pagination
  • Charcoal\Source\SourceConfig
  • Charcoal\Validator\AbstractValidator
  • Charcoal\Validator\ValidatorResult

Interfaces

  • Charcoal\Model\CollectionInterface
  • Charcoal\Model\DescribableInterface
  • Charcoal\Model\MetadataInterface
  • Charcoal\Model\ModelInterface
  • Charcoal\Source\DatabaseSourceInterface
  • Charcoal\Source\FilterInterface
  • Charcoal\Source\OrderInterface
  • Charcoal\Source\PaginationInterface
  • Charcoal\Source\SourceInterface
  • Charcoal\Source\StorableInterface
  • Charcoal\Validator\ValidatableInterface
  • Charcoal\Validator\ValidatorInterface

Traits

  • Charcoal\Model\DescribableTrait
  • Charcoal\Source\StorableTrait
  • Charcoal\Validator\ValidatableTrait
  • Overview
  • Namespace
  • Class
  1: <?php
  2: 
  3: namespace Charcoal\Loader;
  4: 
  5: use \InvalidArgumentException;
  6: 
  7: // Dependencies from PSR-3 (Logger)
  8: use \Psr\Log\LoggerAwareInterface;
  9: use \Psr\Log\LoggerAwareTrait;
 10: use \Psr\Log\NullLogger;
 11: 
 12: /**
 13:  * Base File Loader
 14:  */
 15: class FileLoader implements
 16:     LoggerAwareInterface
 17: {
 18:     use LoggerAwareTrait;
 19: 
 20:     /**
 21:      * The loader's identifier (for caching found paths).
 22:      *
 23:      * @var string
 24:      */
 25:     protected $ident;
 26: 
 27:     /**
 28:      * The paths to search in.
 29:      *
 30:      * @var string[]
 31:      */
 32:     protected $paths = [];
 33: 
 34:     /**
 35:      * The base path to prepend to any relative paths to search in.
 36:      *
 37:      * @var string
 38:      */
 39:     private $basePath = '';
 40: 
 41:     /**
 42:      * Return new FileLoader object.
 43:      *
 44:      * @param array $data The loader's dependencies.
 45:      */
 46:     public function __construct(array $data = null)
 47:     {
 48:         if (isset($data['base_path'])) {
 49:             $this->setBasePath($data['base_path']);
 50:         }
 51: 
 52:         if (isset($data['paths'])) {
 53:             $this->addPaths($data['paths']);
 54:         }
 55: 
 56:         if (!isset($data['logger'])) {
 57:             $data['logger'] = new NullLogger();
 58:         }
 59: 
 60:         $this->setLogger($data['logger']);
 61:     }
 62: 
 63:     /**
 64:      * Retrieve the loader's identifier.
 65:      *
 66:      * @return string
 67:      */
 68:     public function ident()
 69:     {
 70:         return $this->ident;
 71:     }
 72: 
 73:     /**
 74:      * Set the loader's identifier.
 75:      *
 76:      * @param  mixed $ident A subset of language identifiers.
 77:      * @throws InvalidArgumentException If the ident is invalid.
 78:      * @return self
 79:      */
 80:     public function setIdent($ident)
 81:     {
 82:         if (!is_string($ident)) {
 83:             throw new InvalidArgumentException(
 84:                 sprintf(
 85:                     'Identifier for [%1$s] must be a string.',
 86:                     get_called_class()
 87:                 )
 88:             );
 89:         }
 90: 
 91:         $this->ident = $ident;
 92: 
 93:         return $this;
 94:     }
 95: 
 96:     /**
 97:      * Retrieve the base path for relative search paths.
 98:      *
 99:      * @return string
100:      */
101:     public function basePath()
102:     {
103:         return $this->basePath;
104:     }
105: 
106:     /**
107:      * Assign a base path for relative search paths.
108:      *
109:      * @param  string $basePath The base path to use.
110:      * @throws InvalidArgumentException If the base path parameter is not a string.
111:      * @return self
112:      */
113:     public function setBasePath($basePath)
114:     {
115:         if (!is_string($basePath)) {
116:             throw new InvalidArgumentException(
117:                 'Base path must be a string'
118:             );
119:         }
120: 
121:         $basePath = realpath($basePath);
122: 
123:         $this->basePath = rtrim($basePath, '/\\').DIRECTORY_SEPARATOR;
124: 
125:         return $this;
126:     }
127: 
128:     /**
129:      * Returns the content of the first file found in search path.
130:      *
131:      * @param  string|null $ident Optional. A file to search for.
132:      * @return string The file's content or an empty string.
133:      */
134:     public function load($ident = null)
135:     {
136:         if ($ident === null) {
137:             return '';
138:         }
139: 
140:         $fileContent = $this->loadFirstFromSearchPath($ident);
141: 
142:         if ($fileContent) {
143:             return $fileContent;
144:         }
145: 
146:         return '';
147:     }
148: 
149:     /**
150:      * Load the first match from search paths.
151:      *
152:      * @param  string $filename A file to search for.
153:      * @return string|null The matched file's content or an empty string.
154:      */
155:     protected function loadFirstFromSearchPath($filename)
156:     {
157:         $file = $this->firstMatchingFilename($filename);
158: 
159:         if ($file) {
160:             return file_get_contents($file);
161:         }
162: 
163:         return null;
164:     }
165: 
166:     /**
167:      * Retrieve the first match from search paths.
168:      *
169:      * @param  string $filename A file to search for.
170:      * @return string The full path to the matched file.
171:      */
172:     protected function firstMatchingFilename($filename)
173:     {
174:         if (file_exists($filename)) {
175:             return $filename;
176:         }
177: 
178:         $paths = $this->paths();
179: 
180:         if (empty($paths)) {
181:             return null;
182:         }
183: 
184:         foreach ($paths as $path) {
185:             $file = $path.DIRECTORY_SEPARATOR.$filename;
186:             if (file_exists($file)) {
187:                 return $file;
188:             }
189:         }
190: 
191:         return null;
192:     }
193: 
194:     /**
195:      * Retrieve all matches from search paths.
196:      *
197:      * @param  string $filename A file to search for.
198:      * @return array An array of matches.
199:      */
200:     protected function allMatchingFilenames($filename)
201:     {
202:         $matches = [];
203: 
204:         if (file_exists($filename)) {
205:             $matches[] = $filename;
206:         }
207: 
208:         $paths = $this->paths();
209: 
210:         if (empty($paths)) {
211:             return $matches;
212:         }
213: 
214:         foreach ($paths as $path) {
215:             $file = $path.DIRECTORY_SEPARATOR.$filename;
216:             if (file_exists($file)) {
217:                 $matches[] = $file;
218:             }
219:         }
220: 
221:         return $matches;
222:     }
223:     /**
224:      * Load the contents of a JSON file.
225:      *
226:      * @param  mixed $filename The file path to retrieve.
227:      * @throws InvalidArgumentException If a JSON decoding error occurs.
228:      * @return array|null
229:      */
230:     protected function loadJsonFile($filename)
231:     {
232:         $content = file_get_contents($filename);
233: 
234:         if ($content === null) {
235:             return null;
236:         }
237: 
238:         $data  = json_decode($content, true);
239:         $error = json_last_error();
240: 
241:         if ($error == JSON_ERROR_NONE) {
242:             return $data;
243:         }
244: 
245:         switch ($error) {
246:             case JSON_ERROR_NONE:
247:                 break;
248:             case JSON_ERROR_DEPTH:
249:                 $issue = 'Maximum stack depth exceeded';
250:                 break;
251:             case JSON_ERROR_STATE_MISMATCH:
252:                 $issue = 'Underflow or the modes mismatch';
253:                 break;
254:             case JSON_ERROR_CTRL_CHAR:
255:                 $issue = 'Unexpected control character found';
256:                 break;
257:             case JSON_ERROR_SYNTAX:
258:                 $issue = 'Syntax error, malformed JSON';
259:                 break;
260:             case JSON_ERROR_UTF8:
261:                 $issue = 'Malformed UTF-8 characters, possibly incorrectly encoded';
262:                 break;
263:             default:
264:                 $issue = 'Unknown error';
265:                 break;
266:         }
267: 
268:         throw new InvalidArgumentException(
269:             sprintf('JSON %s could not be parsed: "%s"', $filename, $issue)
270:         );
271:     }
272: 
273:     /**
274:      * Retrieve the searchable paths.
275:      *
276:      * @return string[]
277:      */
278:     public function paths()
279:     {
280:         return $this->paths;
281:     }
282: 
283:     /**
284:      * Assign a list of paths.
285:      *
286:      * @param  string[] $paths The list of paths to add.
287:      * @return self
288:      */
289:     public function setPaths(array $paths)
290:     {
291:         $this->paths = [];
292:         $this->addPaths($paths);
293: 
294:         return $this;
295:     }
296: 
297:     /**
298:      * Append a list of paths.
299:      *
300:      * @param  string[] $paths The list of paths to add.
301:      * @return self
302:      */
303:     public function addPaths(array $paths)
304:     {
305:         foreach ($paths as $path) {
306:             $this->addPath($path);
307:         }
308: 
309:         return $this;
310:     }
311: 
312:     /**
313:      * Append a path.
314:      *
315:      * @param  string $path A file or directory path.
316:      * @throws InvalidArgumentException If the path does not exist or is invalid.
317:      * @return self
318:      */
319:     public function addPath($path)
320:     {
321:         $path = $this->resolvePath($path);
322: 
323:         if ($path && $this->validatePath($path)) {
324:             $this->paths[] = $path;
325:         }
326: 
327:         return $this;
328:     }
329: 
330:     /**
331:      * Prepend a path.
332:      *
333:      * @param  string $path A file or directory path.
334:      * @return self
335:      */
336:     public function prependPath($path)
337:     {
338:         $path = $this->resolvePath($path);
339: 
340:         if ($path && $this->validatePath($path)) {
341:             array_unshift($this->paths, $path);
342:         }
343: 
344:         return $this;
345:     }
346: 
347:     /**
348:      * Parse a relative path using the base path if needed.
349:      *
350:      * @param  string $path The path to resolve.
351:      * @throws InvalidArgumentException If the path is invalid.
352:      * @return string
353:      */
354:     public function resolvePath($path)
355:     {
356:         if (!is_string($path)) {
357:             throw new InvalidArgumentException(
358:                 'Path needs to be a string'
359:             );
360:         }
361: 
362:         $basePath = $this->basePath();
363:         $path = ltrim($path, '/\\');
364: 
365:         if ($basePath && strpos($path, $basePath) === false) {
366:             $path = $basePath.$path;
367:         }
368: 
369:         return $path;
370:     }
371: 
372:     /**
373:      * Validate a resolved path.
374:      *
375:      * @param  string $path The path to validate.
376:      * @return boolean Returns TRUE if the path is valid otherwise FALSE.
377:      */
378:     public function validatePath($path)
379:     {
380:         return file_exists($path);
381:     }
382: }
383: 
API documentation generated by ApiGen