diff --git a/core/ClassInfo.php b/core/ClassInfo.php index 39baa1765..7aa17fa56 100644 --- a/core/ClassInfo.php +++ b/core/ClassInfo.php @@ -231,6 +231,25 @@ class ClassInfo { return $matchedClasses; } + + private static $method_from_cache = array(); + + static function has_method_from($class, $method, $compclass) { + if (!isset(self::$method_from_cache[$class])) self::$method_from_cache[$class] = array(); + + if (!array_key_exists($method, self::$method_from_cache[$class])) { + self::$method_from_cache[$class][$method] = false; + + $classRef = new ReflectionClass($class); + + if ($classRef->hasMethod($method)) { + $methodRef = $classRef->getMethod($method); + self::$method_from_cache[$class][$method] = $methodRef->getDeclaringClass()->getName(); + } + } + + return self::$method_from_cache[$class][$method] == $compclass; + } } diff --git a/core/Config.php b/core/Config.php index 6bc413be3..4cf55611b 100644 --- a/core/Config.php +++ b/core/Config.php @@ -173,12 +173,6 @@ class Config { $this->collectConfigPHPSettings = false; } - static $extra_static_sources = array(); - - static function add_static_source($forclass, $donorclass) { - self::$extra_static_sources[$forclass][] = $donorclass; - } - /** @var [Config_ForClass] - The list of Config_ForClass instances, keyed off class */ static protected $for_class_instances = array(); @@ -371,14 +365,17 @@ class Config { // Then look at the static variables $nothing = new stdClass(); - $classes = array($class); + + $sources = array($class); // Include extensions only if not flagged not to, and some have been set - if ((($sourceOptions & self::EXCLUDE_EXTRA_SOURCES) != self::EXCLUDE_EXTRA_SOURCES) && isset(self::$extra_static_sources[$class])) { - $classes = array_merge($classes, self::$extra_static_sources[$class]); + if (($sourceOptions & self::EXCLUDE_EXTRA_SOURCES) != self::EXCLUDE_EXTRA_SOURCES) { + $extraSources = Object::get_extra_config_sources($class); + if ($extraSources) $sources = array_merge($sources, $extraSources); } - foreach ($classes as $staticSource) { - $value = Object::static_lookup($staticSource, $name, $nothing); + foreach ($sources as $staticSource) { + if (is_array($staticSource)) $value = isset($staticSource[$name]) ? $staticSource[$name] : $nothing; + else $value = Object::static_lookup($staticSource, $name, $nothing); if ($value !== $nothing) { self::merge_low_into_high($result, $value, $suppress); diff --git a/core/Extension.php b/core/Extension.php index 7eb2a8ce7..70072616f 100644 --- a/core/Extension.php +++ b/core/Extension.php @@ -46,14 +46,11 @@ abstract class Extension { /** * Called when this extension is added to a particular class * - * TODO: This is likely to be replaced by event sytem before 3.0 final, so be aware - * this API is fairly unstable. - * * @static * @param $class */ static function add_to_class($class, $extensionClass, $args = null) { - Config::add_static_source($class, $extensionClass); + // NOP } /** diff --git a/core/Object.php b/core/Object.php index b37423762..1b7f98a07 100755 --- a/core/Object.php +++ b/core/Object.php @@ -463,6 +463,7 @@ abstract class Object { if($subclasses) foreach($subclasses as $subclass) { unset(self::$classes_constructed[$subclass]); unset(self::$extra_methods[$subclass]); + unset(self::$extension_sources[$subclass]); } Config::inst()->update($class, 'extensions', array($extension)); @@ -505,6 +506,7 @@ abstract class Object { if($subclasses) foreach($subclasses as $subclass) { unset(self::$classes_constructed[$subclass]); unset(self::$extra_methods[$subclass]); + unset(self::$extension_sources[$subclass]); } } @@ -531,38 +533,66 @@ abstract class Object { // ----------------------------------------------------------------------------------------------------------------- - private static $_added_extensions = array(); + private static $extension_sources = array(); + + // Don't bother checking some classes that should never be extended + private static $unextendable_classes = array('Object', 'ViewableData', 'RequestHandler'); + + static public function get_extra_config_sources($class = null) { + if($class === null) $class = get_called_class(); + + // If this class is unextendable, NOP + if(in_array($class, self::$unextendable_classes)) return; + + // If we have a pre-cached version, use that + if(array_key_exists($class, self::$extension_sources)) return self::$extension_sources[$class]; + + // Variable to hold sources in + $sources = null; + + // Get a list of extensions + $extensions = Config::inst()->get($class, 'extensions', Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES); + + if($extensions) { + // Build a list of all sources; + $sources = array(); + + foreach($extensions as $extension) { + list($extensionClass, $extensionArgs) = self::parse_class_spec($extension); + $sources[] = $extensionClass; + + if(!ClassInfo::has_method_from($extensionClass, 'add_to_class', 'Extension')) { + Deprecation::notice('3.1.0', "add_to_class deprecated on $extensionClass. Use get_extra_config instead"); + } + + call_user_func(array($extensionClass, 'add_to_class'), $class, $extensionClass, $extensionArgs); + + foreach(array_reverse(ClassInfo::ancestry($extensionClass)) as $extensionClassParent) { + if (ClassInfo::has_method_from($extensionClassParent, 'get_extra_config', $extensionClassParent)) { + $extras = $extensionClassParent::get_extra_config($class, $extensionClass, $extensionArgs); + if ($extras) $sources[] = $extras; + } + } + } + } + + return self::$extension_sources[$class] = $sources; + } public function __construct() { $this->class = get_class($this); - // Don't bother checking some classes that should never be extended - static $notExtendable = array('Object', 'ViewableData', 'RequestHandler'); - - if($extensionClasses = ClassInfo::ancestry($this->class)) foreach($extensionClasses as $class) { - if(in_array($class, $notExtendable)) continue; - if($extensions = Config::inst()->get($class, 'extensions', Config::UNINHERITED)) { - foreach($extensions as $extension) { - // Get the extension class for this extension - list($extensionClass, $extensionArgs) = self::parse_class_spec($extension); + foreach(ClassInfo::ancestry(get_called_class()) as $class) { + if(in_array($class, self::$unextendable_classes)) continue; + $extensions = Config::inst()->get($class, 'extensions', Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES); - // If we haven't told that extension it's attached to this class yet, do that now - if (!isset(self::$_added_extensions[$extensionClass][$class])) { - // First call the add_to_class method - this will inherit down & is defined on Extension, so if not defined, no worries - call_user_func(array($extensionClass, 'add_to_class'), $class, $extensionClass, $extensionArgs); - - // Then register it as having been told about us - if (!isset(self::$_added_extensions[$extensionClass])) self::$_added_extensions[$extensionClass] = array($class => true); - else self::$_added_extensions[$extensionClass][$class] = true; - } - - $instance = self::create_from_string($extension); - $instance->setOwner(null, $class); - $this->extension_instances[$instance->class] = $instance; - } + if($extensions) foreach($extensions as $extension) { + $instance = self::create_from_string($extension); + $instance->setOwner(null, $class); + $this->extension_instances[$instance->class] = $instance; } } - + if(!isset(self::$classes_constructed[$this->class])) { $this->defineMethods(); self::$classes_constructed[$this->class] = true; diff --git a/model/DataExtension.php b/model/DataExtension.php index b2dae0611..d975f7db2 100644 --- a/model/DataExtension.php +++ b/model/DataExtension.php @@ -31,34 +31,21 @@ abstract class DataExtension extends Extension { 'api_access' => false, ); - static function add_to_class($class, $extensionClass, $args = null) { - if(method_exists($class, 'extraDBFields')) { + static function get_extra_config($class, $extension, $args) { + if(method_exists($extension, 'extraDBFields')) { $extraStaticsMethod = 'extraDBFields'; } else { $extraStaticsMethod = 'extraStatics'; } - $statics = Injector::inst()->get($extensionClass, true, $args)->$extraStaticsMethod($class, $extensionClass); + $statics = Injector::inst()->get($extension, true, $args)->$extraStaticsMethod(); if ($statics) { - Deprecation::notice('3.1.0', "$extraStaticsMethod deprecated. Just define statics on your extension, or use add_to_class", Deprecation::SCOPE_GLOBAL); - - // TODO: This currently makes extraStatics the MOST IMPORTANT config layer, not the least - foreach (self::$extendable_statics as $key => $merge) { - if (isset($statics[$key])) { - if (!$merge) Config::inst()->remove($class, $key); - Config::inst()->update($class, $key, $statics[$key]); - } - } - - // TODO - remove this - DataObject::$cache_has_own_table[$class] = null; - DataObject::$cache_has_own_table_field[$class] = null; + Deprecation::notice('3.1.0', "$extraStaticsMethod deprecated. Just define statics on your extension, or use get_extra_config", Deprecation::SCOPE_GLOBAL); + return $statics; } - - parent::add_to_class($class, $extensionClass, $args); } - + public static function unload_extra_statics($class, $extension) { throw new Exception('unload_extra_statics gone'); } diff --git a/model/Hierarchy.php b/model/Hierarchy.php index d6100fa29..3bcc25a20 100644 --- a/model/Hierarchy.php +++ b/model/Hierarchy.php @@ -25,9 +25,10 @@ class Hierarchy extends DataExtension { function augmentWrite(&$manipulation) { } - static function add_to_class($class, $extensionClass, $args = null) { - Config::inst()->update($class, 'has_one', array('Parent' => $class)); - parent::add_to_class($class, $extensionClass, $args); + static function get_extra_config($class, $extension, $args) { + return array( + 'has_one' => array('Parent' => $class) + ); } /** diff --git a/model/Versioned.php b/model/Versioned.php index 67dedb5bd..01e583ad5 100644 --- a/model/Versioned.php +++ b/model/Versioned.php @@ -107,9 +107,10 @@ class Versioned extends DataExtension { 'Version' => 'Int' ); - static function add_to_class($class, $extensionClass, $args = null) { - Config::inst()->update($class, 'has_many', array('Versions' => $class)); - parent::add_to_class($class, $extensionClass, $args); + static function get_extra_config($class, $extension, $args) { + array( + 'has_many' => array('Versions' => $class) + ); } /** diff --git a/search/FulltextSearchable.php b/search/FulltextSearchable.php index 77f660fb1..006ac028b 100644 --- a/search/FulltextSearchable.php +++ b/search/FulltextSearchable.php @@ -75,14 +75,16 @@ class FulltextSearchable extends DataExtension { parent::__construct(); } - static function add_to_class($class, $extensionClass, $args = null) { - Config::inst()->update($class, 'indexes', array('SearchFields' => array( - 'type' => 'fulltext', - 'name' => 'SearchFields', - 'value' => $args[0] - ))); - - parent::add_to_class($class, $extensionClass, $args); + static function get_extra_config($class, $extensionClass, $args) { + return array( + 'indexes' => array( + 'SearchFields' => array( + 'type' => 'fulltext', + 'name' => 'SearchFields', + 'value' => $args[0] + ) + ) + ); } /**