Index: branches/5.3.x/core/kernel/utility/factory.php =================================================================== diff -u -N -r16156 -r16163 --- branches/5.3.x/core/kernel/utility/factory.php (.../factory.php) (revision 16156) +++ branches/5.3.x/core/kernel/utility/factory.php (.../factory.php) (revision 16163) @@ -1,6 +1,6 @@ get(); + list($class_map, $class_info) = $class_map_builder->get(); $class_names = array_keys($class_map); $this->classMap = array_merge($this->classMap, $class_map); + $this->classInfo = array_merge($this->classInfo, $class_info); $this->realClasses = array_merge($this->realClasses, array_combine($class_names, $class_names)); + + foreach ( $class_info as $class => $class_data ) { + if ( isset($class_data['extends']) ) { + foreach ( $class_data['extends'] as $extends_class ) { + if ( !isset($this->classTree[$extends_class]) ) { + $this->classTree[$extends_class] = array(); + } + + $this->classTree[$extends_class][] = $class; + } + } + } } } @@ -109,6 +143,8 @@ public function setFromCache(&$data) { $this->classMap = $data['Factory.Files']; + $this->classInfo = $data['Factory.ClassInfo']; + $this->classTree = $data['Factory.ClassTree']; $this->namespaceMap = $data['Factory.Namespaces']; $this->realClasses = $data['Factory.realClasses']; } @@ -191,11 +227,15 @@ public function getToCache() { ksort($this->classMap); + ksort($this->classInfo); + ksort($this->classTree); ksort($this->namespaceMap); ksort($this->realClasses); return Array ( 'Factory.Files' => $this->classMap, + 'Factory.ClassInfo' => $this->classInfo, + 'Factory.ClassTree' => $this->classTree, 'Factory.Namespaces' => $this->namespaceMap, 'Factory.realClasses' => $this->realClasses, ); @@ -352,6 +392,65 @@ } /** + * Returns sub-classes of given ancestor class. + * + * @param string $ancestor_class Ancestor class. + * @param boolean $concrete_only Return only non-abstract classes. + * + * @return array + * @throws kFactoryException When ancestor class not found. + */ + public function getSubClasses($ancestor_class, $concrete_only = true) + { + if ( !isset($this->classMap[$ancestor_class]) ) { + throw new kFactoryException( + 'Class "' . $ancestor_class . '" is not registered in the Factory' + ); + } + + if ( !isset($this->classTree[$ancestor_class]) ) { + return array(); + } + + $all_sub_classes = array(); + + foreach ( $this->classTree[$ancestor_class] as $sub_class ) { + $real_sub_class = $this->realClasses[$sub_class]; + $all_sub_classes[$real_sub_class] = $sub_class; + $all_sub_classes = array_merge($all_sub_classes, $this->getSubClasses($sub_class, false)); + } + + if ( $concrete_only ) { + $concrete_sub_classes = array(); + + foreach ( $all_sub_classes as $real_sub_class => $sub_class ) { + if ( $this->classInfo[$sub_class]['type'] == self::TYPE_CLASS + && !$this->classHasModifier($sub_class, self::MODIFIER_ABSTRACT) + ) { + $concrete_sub_classes[$real_sub_class] = $sub_class; + } + } + + return $concrete_sub_classes; + } + + return $all_sub_classes; + } + + /** + * Determines of class has modifier. + * + * @param string $class Class. + * @param integer $modifier Modifier. + * + * @return boolean + */ + protected function classHasModifier($class, $modifier) + { + return ($this->classInfo[$class]['modifiers'] & $modifier) == $modifier; + } + + /** * Registers new class in the factory * * @param string $real_class Real name of class as in class declaration