Chris@0: directories = $directories; Chris@0: $this->fileCacheKeySuffix = $file_cache_key_suffix; Chris@0: $this->idKey = $key; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function findAll() { Chris@0: $all = []; Chris@0: Chris@0: $files = $this->findFiles(); Chris@0: Chris@0: $file_cache = FileCacheFactory::get('yaml_discovery:' . $this->fileCacheKeySuffix); Chris@0: Chris@0: // Try to load from the file cache first. Chris@0: foreach ($file_cache->getMultiple(array_keys($files)) as $file => $data) { Chris@0: $all[$files[$file]][$this->getIdentifier($file, $data)] = $data; Chris@0: unset($files[$file]); Chris@0: } Chris@0: Chris@0: // If there are files left that were not returned from the cache, load and Chris@0: // parse them now. This list was flipped above and is keyed by filename. Chris@0: if ($files) { Chris@0: foreach ($files as $file => $provider) { Chris@0: // If a file is empty or its contents are commented out, return an empty Chris@0: // array instead of NULL for type consistency. Chris@0: try { Chris@0: $data = Yaml::decode(file_get_contents($file)) ?: []; Chris@0: } Chris@0: catch (InvalidDataTypeException $e) { Chris@0: throw new DiscoveryException("The $file contains invalid YAML", 0, $e); Chris@0: } Chris@0: $data[static::FILE_KEY] = $file; Chris@0: $all[$provider][$this->getIdentifier($file, $data)] = $data; Chris@0: $file_cache->set($file, $data); Chris@0: } Chris@0: } Chris@0: Chris@0: return $all; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the identifier from the data. Chris@0: * Chris@0: * @param string $file Chris@0: * The filename. Chris@0: * @param array $data Chris@0: * The data from the YAML file. Chris@0: * Chris@0: * @return string Chris@0: * The identifier from the data. Chris@0: */ Chris@0: protected function getIdentifier($file, array $data) { Chris@0: if (!isset($data[$this->idKey])) { Chris@0: throw new DiscoveryException("The $file contains no data in the identifier key '{$this->idKey}'"); Chris@0: } Chris@0: return $data[$this->idKey]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns an array of providers keyed by file path. Chris@0: * Chris@0: * @return array Chris@0: * An array of providers keyed by file path. Chris@0: */ Chris@0: protected function findFiles() { Chris@0: $file_list = []; Chris@0: foreach ($this->directories as $provider => $directories) { Chris@0: $directories = (array) $directories; Chris@0: foreach ($directories as $directory) { Chris@0: if (is_dir($directory)) { Chris@0: /** @var \SplFileInfo $fileInfo */ Chris@0: foreach ($this->getDirectoryIterator($directory) as $fileInfo) { Chris@0: $file_list[$fileInfo->getPathname()] = $provider; Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: return $file_list; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets an iterator to loop over the files in the provided directory. Chris@0: * Chris@0: * This method exists so that it is easy to replace this functionality in a Chris@0: * class that extends this one. For example, it could be used to make the scan Chris@0: * recursive. Chris@0: * Chris@0: * @param string $directory Chris@0: * The directory to scan. Chris@0: * Chris@0: * @return \Traversable Chris@0: * An \Traversable object or array where the values are \SplFileInfo Chris@0: * objects. Chris@0: */ Chris@0: protected function getDirectoryIterator($directory) { Chris@0: return new RegexDirectoryIterator($directory, '/\.yml$/i'); Chris@0: } Chris@0: Chris@0: }