diff --git a/src/Files/FilesEntry.php b/src/Files/FilesEntry.php index a28a502..563360a 100644 --- a/src/Files/FilesEntry.php +++ b/src/Files/FilesEntry.php @@ -5,14 +5,25 @@ class FilesEntry { private $file; - public function __construct($fileToInclude) + private $php; + + public function __construct($fileToInclude, $php) { - $this->file = $fileToInclude; + $this->file = $this->normalizePath($fileToInclude); + $this->php = $php; } public function knowsNamespace($namespace) { - // TODO. + $classes = $this->getClassesInFile(); + + foreach($classes as $class) { + if (strpos($class, $namespace) !== false) { + return true; + }; + } + + return false; } /** @@ -23,4 +34,43 @@ class FilesEntry { // TODO. } + + /** + * This is where the real magic happens. Since classes in a randomly included file could contain classes in any namespace, + * (or even multiple namespaces!) we must execute the file and check for newly defined classes. This has a potential + * downside that files being executed will execute their side effects - which may be undesirable. However, Composer + * will require these files anyway - so hopefully causing those side effects isn't that big of a deal. + * execute the + * @return array + */ + private function getClassesInFile() + { + // get_declared_classes() returns a bunch of classes that are built into PHP. So we need a control here. + $script = "var_export(get_declared_classes());"; + exec($this->php . " -r \"$script\"", $output); + $classes = 'return ' . implode('', $output) . ';'; + $initialClasses = eval($classes); + + // clear the exec() buffer. + unset($output); + + // This brings in the new classes. so $classes here will include the PHP defaults and the newly defined classes + $script = "require_once '{$this->file}'; var_export(get_declared_classes());"; + exec($this->php . ' -r "' . $script . '"', $output); + $classes = 'return ' . implode('', $output) . ';'; + $allClasses = eval($classes); + + return array_diff($allClasses, $initialClasses); + } + + /** + * TODO: Similar to PSR4Namespace::normalizePath. Maybe we refactor? + * @param $path + * @return mixed + */ + public function normalizePath($path) + { + $path = str_replace('\\', '/', $path); + return $path; + } } \ No newline at end of file diff --git a/src/Files/FilesEntryFactory.php b/src/Files/FilesEntryFactory.php new file mode 100644 index 0000000..24b1e90 --- /dev/null +++ b/src/Files/FilesEntryFactory.php @@ -0,0 +1,56 @@ +appConfig = $appConfig; + } + + /** + * @return array + * @throws ClassFinderException + */ + public function getFilesEntries() + { + $files = require($this->appConfig->getAppRoot() . 'vendor/composer/autoload_files.php'); + $phpPath = $this->findPHP(); + + $filesKeys = array_values($files); + return array_map(function($index) use ($filesKeys, $phpPath){ + return new FilesEntry($filesKeys[$index], $phpPath); + }, range(0, count($files) - 1)); + } + + /** + * Locates the PHP interrupter. If PHP 5.4 or newer is used, the PHP_BINARY value is used. Otherwise we attempt to + * find it from shell commands. + * @return string + * @throws ClassFinderException + */ + private function findPHP() + { + if (defined(PHP_BINARY)) { + // PHP_BINARY was made available in PHP 5.4 + $php = PHP_BINARY; + } else { + if (exec('which php', $output)) { + $php = $output[0]; + } elseif (exec('where php', $output)) { + $php = $output[0]; + } else { + // TODO: Add link to docs for this. + throw new ClassFinderException('Could not locate PHP interrupter.'); + } + } + + return $php; + } +} \ No newline at end of file diff --git a/src/Files/FilesFactory.php b/src/Files/FilesFactory.php deleted file mode 100644 index 329dac3..0000000 --- a/src/Files/FilesFactory.php +++ /dev/null @@ -1,28 +0,0 @@ -appConfig = $appConfig; - } - - /** - * @return array - */ - public function getFilesEntries() - { - $files = array(); - - $filesKeys = array_keys($files); - return array_map(function($index) use ($filesKeys){ - return new FilesEntry($filesKeys[$index]); - }, range(0, count($files) - 1)); - } -} \ No newline at end of file diff --git a/src/Files/FilesFinder.php b/src/Files/FilesFinder.php index ef3c05f..3e6c313 100644 --- a/src/Files/FilesFinder.php +++ b/src/Files/FilesFinder.php @@ -14,7 +14,7 @@ class FilesFinder implements FinderInterface public function isNamespaceKnown($namespace) { - $filesEntries = $this->factory->getfilesEntries(); + $filesEntries = $this->factory->getFilesEntries(); foreach($filesEntries as $filesEntry) { if ($filesEntry->knowsNamespace($namespace)) { @@ -33,11 +33,11 @@ class FilesFinder implements FinderInterface { $filesEntries = $this->factory->getFilesEntries(); - $matchingEntries = array_filter($filesEntries, function(filesEntry $entry) use ($namespace) { + $matchingEntries = array_filter($filesEntries, function(FilesEntry $entry) use ($namespace) { return $entry->matches($namespace); }); - return array_map(function(filesEntry $entry) { + return array_map(function(FilesEntry $entry) { return $entry->getClassName(); }, $matchingEntries); } diff --git a/test/app1/files/files2.php b/test/app1/files/files2.php index b1cb8d6..6232a58 100644 --- a/test/app1/files/files2.php +++ b/test/app1/files/files2.php @@ -1,10 +1,10 @@ assertFalse(true, 'An exception occurred: ' . $e->getMessage());