diff --git a/src/Discovery/PathFinderBase.php b/src/Discovery/PathFinderBase.php index 41ece1c..7f2bd6f 100644 --- a/src/Discovery/PathFinderBase.php +++ b/src/Discovery/PathFinderBase.php @@ -8,7 +8,7 @@ namespace Drupal\Composer\ClassLoader\Discovery; -class PathFinderBase implements PathFinderInterface { +abstract class PathFinderBase implements PathFinderInterface { /** * The relative path. @@ -27,4 +27,12 @@ public function __construct($path) { $this->path = $path; } + /** + * {@inheritdoc} + */ + public function requireFile($seed) { + $real_path = $this->find($seed); + require_once $real_path; + } + } diff --git a/src/Discovery/PathFinderContrib.php b/src/Discovery/PathFinderContrib.php index cdd6f54..37ecf4a 100644 --- a/src/Discovery/PathFinderContrib.php +++ b/src/Discovery/PathFinderContrib.php @@ -8,6 +8,66 @@ namespace Drupal\Composer\ClassLoader\Discovery; +use Drupal\Composer\ClassLoader\ClassLoaderException; + class PathFinderContrib extends PathFinderBase implements PathFinderInterface { + /** + * The module name. + * + * @var string + */ + protected $moduleName; + + /** + * Constructs a PathFinderContrib object. + * + * @param string $path + * The relative path to find. + * @param string $module_name + * The name of the contrib module to find. + */ + public function __construct($path, $module_name) { + $this->path = $path; + $this->moduleName = $module_name; + } + + /** + * {@inheritdoc} + */ + public function find($seed) { + $core_finder = new PathFinderCore(''); + if (!$core_path = $core_finder->find($seed)) { + return NULL; + } + $core_directory = new \RecursiveDirectoryIterator($core_path . '/sites', \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS); + $files_iterator = new \RecursiveIteratorIterator($core_directory, \RecursiveIteratorIterator::SELF_FIRST); + // Iterate over all of the directories under the sites directory. + foreach ($files_iterator as $path_name => $dir) { + /** @var $dir \SplFileInfo */ + if (!$dir->isDir()) { + continue; + } + // Check if the current directory corresponds to the contrib we are + // looking for. + if ($this->isWantedContrib($dir)) { + return $dir->getPathname() . '/' . $this->path; + } + } + throw new ClassLoaderException(sprintf('Drupal module "%s" could not be found in the Drupal tree that contains: %s.', $this->moduleName, $seed)); + } + + /** + * Checks if the passed directory is the contrib module we are looking for. + * + * @param \SplFileInfo $dir + * The info object about the directory. + * @return bool + * TRUE if the contrib is detected. FALSE otherwise. + */ + protected function isWantedContrib(\SplFileInfo $dir) { + $info_file = $dir->getPathname() . '/' . $this->moduleName . '.info'; + return file_exists($info_file); + } + } diff --git a/src/Discovery/PathFinderCore.php b/src/Discovery/PathFinderCore.php index 4f9c78f..2a353c8 100644 --- a/src/Discovery/PathFinderCore.php +++ b/src/Discovery/PathFinderCore.php @@ -7,7 +7,73 @@ namespace Drupal\Composer\ClassLoader\Discovery; +use Drupal\Composer\ClassLoader\ClassLoaderException; class PathFinderCore extends PathFinderBase implements PathFinderInterface { + /** + * {@inheritdoc} + */ + public function find($seed) { + $seed = realpath($seed); + // Try to create the iterator with the seed. + try { + $directory = new \DirectoryIterator($seed); + } + catch (\UnexpectedValueException $e) { + // If the seed was not a directory, then get the parent directory. + $path_info = pathinfo($seed); + $directory = new \DirectoryIterator($path_info['dirname']); + } + do { + if ($this->isDrupalRoot($directory)) { + return $directory . '/' . $this->path; + } + } + while ($directory = $this->getParentDirectory($directory)); + throw new ClassLoaderException(sprintf('Drupal core directory could not be found as a parent of: %s.', $seed)); + } + + /** + * Checks if the passed directory is the Drupal root. + * + * @param \DirectoryIterator $directory + * The directory iterator item. + * + * @return bool + * TRUE if the passed directory is the Drupal root. + */ + protected function isDrupalRoot(\DirectoryIterator $directory) { + // Check if there is a COPYRIGHT.txt file in the directory. + foreach ($directory as $item) { + if (!$item->isFile() && $item->getFilename() != 'COPYRIGHT.txt') { + continue; + } + $line = fgets(fopen($item->getPathname(), 'r')); + return strpos($line, 'All Drupal code is Copyright') === 0; + } + return FALSE; + } + + /** + * Gets the parent directory iterator. + * + * @param \DirectoryIterator $directory + * The current directory iterator. + * + * @return \DirectoryIterator + * The parent directory. + */ + protected function getParentDirectory(\DirectoryIterator $directory) { + $path_name = $directory->getPathname(); + $path_info = pathinfo($path_name); + if (!empty($path_info['dirname'])) { + try { + return new \DirectoryIterator($path_info['dirname']); + } + catch (\UnexpectedValueException $e) {} + } + return NULL; + } + } diff --git a/src/Discovery/PathFinderInterface.php b/src/Discovery/PathFinderInterface.php index 4a871af..382b2f1 100644 --- a/src/Discovery/PathFinderInterface.php +++ b/src/Discovery/PathFinderInterface.php @@ -10,4 +10,27 @@ interface PathFinderInterface { + /** + * Finds and requires the current file. + * + * @throws \Drupal\Composer\ClassLoader\ClassLoaderException + * If the file could not be found. + */ + public function requireFile($seed); + + /** + * Finds the current file in the file system. + * + * @param string $seed + * A path where to start looking for. Typically it will be the composer.json + * file name that contains the configuration. + * + * @throws \Drupal\Composer\ClassLoader\ClassLoaderException + * If the file could not be found. + * + * @return string + * The name of the real path. NULL if the file was not found. + */ + public function find($seed); + } diff --git a/src/Discovery/PathFinderNull.php b/src/Discovery/PathFinderNull.php index 00d39c6..f7af936 100644 --- a/src/Discovery/PathFinderNull.php +++ b/src/Discovery/PathFinderNull.php @@ -12,9 +12,8 @@ class PathFinderNull extends PathFinderBase implements PathFinderInterface { /** * {@inheritdoc} */ - public function requireFile() { - require_once $this->path; - return TRUE; + public function find($seed) { + return $this->path; } } diff --git a/src/Loader.php b/src/Loader.php index cbf372a..ad18530 100644 --- a/src/Loader.php +++ b/src/Loader.php @@ -27,11 +27,17 @@ public static function autoload($class) { if (!in_array($class, static::$classMap)) { return FALSE; } - $resolver = new TokenResolver(static::$classMap[$class]); - $finder = $resolver->resolve(); - // Have the path finder require the file and return TRUE or FALSE if it - // found the file or not. - return $finder->require(); + try { + $resolver = new TokenResolver(static::$classMap[$class]); + $finder = $resolver->resolve(); + // Have the path finder require the file and return TRUE or FALSE if it + // found the file or not. + $finder->requireFile(); + return TRUE; + } + catch (ClassLoaderException $e) { + return FALSE; + } } /**