1
0
mirror of https://github.com/danog/class-finder.git synced 2025-01-22 13:51:42 +01:00

Implement the mechanism that determines the classes in a file; implement FileEntry::knowsNamespace.

This commit is contained in:
Hayden Pierce 2018-10-20 20:57:53 -05:00
parent 31a4b109ae
commit 4a91665f3a
6 changed files with 117 additions and 38 deletions

View File

@ -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;
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace HaydenPierce\ClassFinder\Files;
use HaydenPierce\ClassFinder\AppConfig;
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
class FilesEntryFactory
{
/** @var AppConfig */
private $appConfig;
public function __construct(AppConfig $appConfig)
{
$this->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;
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace HaydenPierce\ClassFinder\Files;
use HaydenPierce\ClassFinder\AppConfig;
class FilesEntryFactory
{
/** @var AppConfig */
private $appConfig;
public function __construct(AppConfig $appConfig)
{
$this->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));
}
}

View File

@ -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);
}

View File

@ -1,10 +1,10 @@
<?php
// This is intentionally made global. There are intentionally classes here in and out of a namespace.
// PHPStorm is subborn in it's complaining about this - so I just turn off inspections for this file.
class my_global_class_name {}
namespace TestApp1\FilesClasses;
class Cam {}
class Lam {}
namespace TestApp1\RedHerring;
class RedHerring {}

View File

@ -19,6 +19,7 @@ class FilesTest extends \PHPUnit_Framework_TestCase
public function testClassFinder($namespace, $expected, $message)
{
try {
ClassFinder::enableFilesSupport();
$classes = ClassFinder::getClassesInNamespace($namespace);
} catch (\Exception $e) {
$this->assertFalse(true, 'An exception occurred: ' . $e->getMessage());