From af38940a5d16e6687c9f56a27d39f8c8a2eeccc2 Mon Sep 17 00:00:00 2001 From: m4rw3r Date: Tue, 19 Apr 2011 13:47:32 +0200 Subject: [PATCH] Made the ClassLoader throw an exception on duplicate classes, class-list can still be retrieved, also made the ExtensionsFinder handle this exception and write out an error message before proceeding with loading the found files --- .../Phix/ClassConflictException.php | 113 ++++++++++++++++++ src/php/Phix_Project/Phix/ClassFinder.php | 39 +++++- .../Phix_Project/Phix/ExtensionsFinder.php | 19 ++- src/php/Phix_Project/Phix/Phix.php | 2 +- 4 files changed, 165 insertions(+), 8 deletions(-) create mode 100644 src/php/Phix_Project/Phix/ClassConflictException.php diff --git a/src/php/Phix_Project/Phix/ClassConflictException.php b/src/php/Phix_Project/Phix/ClassConflictException.php new file mode 100644 index 0000000..7311580 --- /dev/null +++ b/src/php/Phix_Project/Phix/ClassConflictException.php @@ -0,0 +1,113 @@ + + * @copyright 2010 Martin Wernståhl + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://github.com/m4rw3r + * @version @@PACKAGE_VERSION@@ + */ + +namespace Phix_Project\Phix; + +/** + * Exception telling that a conflicting class name was discovered + * by the class finder. + */ +class ClassConflictException extends \RuntimeException +{ + // TODO: Is this threshold good? + const MAX_MESSAGE_LENGTH = 40; + + protected $classes = array(); + + protected $files = array(); + + /** + * @param array(array('class' => string, 'file' => string), ...) + */ + function __construct(array $conflicts) + { + $this->classes = array_map(function($elem) + { + return $elem['class']; + }, $conflicts); + + $this->files = array_map(function($elem) + { + return $elem['file']; + }, $conflicts); + + $classlist = implode(', ', $this->classes); + $filelist = implode(', ', $this->files); + + if(strlen($classlist) > static::MAX_MESSAGE_LENGTH) + { + $classlist = substr($classlist, 0, static::MAX_MESSAGE_LENGTH).'...'; + } + + if(strlen($filelist) > static::MAX_MESSAGE_LENGTH) + { + $filelist = substr($filelist, 0, static::MAX_MESSAGE_LENGTH).'...'; + } + + parent::__construct('ClassFinder: Found conflicting class(es): '.$classlist.' in files: '.$filelist); + } + + // ------------------------------------------------------------------------ + + /** + * Returns a list of unique class names which were conflicted. + * + * @return array(string) + */ + public function getConflictingClasses() + { + return array_unique($this->classes); + } + + // ------------------------------------------------------------------------ + + /** + * Returns a list of files containing conflicting classes. + * + * @return array(string) + */ + public function getConflictingFiles() + { + return array_unique($this->files); + } +} diff --git a/src/php/Phix_Project/Phix/ClassFinder.php b/src/php/Phix_Project/Phix/ClassFinder.php index cbd4c54..cfe4388 100644 --- a/src/php/Phix_Project/Phix/ClassFinder.php +++ b/src/php/Phix_Project/Phix/ClassFinder.php @@ -47,8 +47,27 @@ * Searches through a set of paths for classes, returns a list of classes * and in which file they are located, does NOT include the files. * - * If duplicate classes are found, returns only the first file that - * contains the class + * Throws a PHP exception if duplicate files are found, but the classes + * can still be found by calling getClassFiles() again but won't contain + * the duplicates (only the first occurrence of the class). + * + * Example usage: + * + * $finder = new \Phix_Project\Phix\ClassFinder(array('.', '../src')); + * + * try + * { + * $classes = $finder->getClassFiles(); + * } + * catch(\Phix_Project\Phix\ClassConflictException $e) + * { + * // Take note of the error: + * echo "Conflicting classes!"; + * + * // Let the operation continue, but might contain the wrong class-file: + * $classes = $finder->getClassFiles(); + * } + * */ class ClassFinder @@ -108,6 +127,8 @@ public function getClassFiles() return $this->list; } + $conflicts = array(); + foreach($this->paths as $path) { // Search the folder @@ -119,15 +140,23 @@ public function getClassFiles() { foreach($this->getClasses($name) as $class) { - if(!isset($this->list[$class])) + if(isset($this->list[$class])) + { + $conflicts[] = array('class' => $class, 'file' => $name); + } + else { - // class is new! - $this->list[$class] = $name; + $this->list[$class] = $name; } } } } + if( ! empty($conflicts)) + { + throw new ClassConflictException($conflicts); + } + return $this->list; } diff --git a/src/php/Phix_Project/Phix/ExtensionsFinder.php b/src/php/Phix_Project/Phix/ExtensionsFinder.php index d081523..c548cb6 100644 --- a/src/php/Phix_Project/Phix/ExtensionsFinder.php +++ b/src/php/Phix_Project/Phix/ExtensionsFinder.php @@ -46,10 +46,13 @@ class ExtensionsFinder { + protected $context; + protected $foldersToSearch = array(); - public function __construct() + public function __construct(Context $context) { + $this->context = $context; $this->addPhpSearchPathToSearchList(); } @@ -72,8 +75,20 @@ public function findExtensions() // Find all classes in php files whose paths contain "PhixCommands": $classFinder = new ClassFinder(explode(\PATH_SEPARATOR, \get_include_path()), '/PhixCommands.*\.php$/'); + + try + { + $files = $classFinder->getClassFiles(); + } + catch(ClassConflictException $e) + { + $this->context->stderr->output($this->context->errorStyle, $this->context->errorPrefix); + $this->context->stderr->outputLine(null, $e->getMessage()); + + $files = $classFinder->getClassFiles(); + } - foreach ($classFinder->getClassFiles() as $newClass => $filename) + foreach ($files as $newClass => $filename) { include_once $filename; diff --git a/src/php/Phix_Project/Phix/Phix.php b/src/php/Phix_Project/Phix/Phix.php index e6fb848..075cb11 100644 --- a/src/php/Phix_Project/Phix/Phix.php +++ b/src/php/Phix_Project/Phix/Phix.php @@ -160,7 +160,7 @@ protected function processPhixSwitchesBeforeExtensionLoad(Context $context, Pars protected function loadPhixExtensions(Context $context, ParsedSwitches $ParsedSwitches) { // create something to find the commands - $extensionsFinder = new ExtensionsFinder(); + $extensionsFinder = new ExtensionsFinder($context); // seed the commandsFinder with a list of where to look // if the user has given us any hints