Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

API Reverse config extra statics control flow

Config system used to provide an add_static_source method, which was intended for
use by Extensions to add statics. But extensions for a class arent initialised
until at least one instance of that class is created, so before that the
Config system didnt include values from extensions

This patch reverses the control flow, so that the Config system explictly asks
each Object for its additional config sources via the new method
get_extra_config_sources. This method returns an array that can contain
string names of classes and also raw associative arrays.

The developer visible change is that Extension#add_to_class has been
deprecated. Instead there is a new method, get_extra_config, which has
the same method signature but needs to guarantee that it doesnt
cause side effects. Additionally there is no need to call
parent::get_extra_config - this is handled automatically.
  • Loading branch information...
commit fa37c448a5ce22330be8985acf1a71769e472a2c 1 parent 4916b36
Hamish Friedlander hafriedlander authored
19 core/ClassInfo.php
View
@@ -231,6 +231,25 @@ static function classes_for_folder($folderPath) {
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;
+ }
}
19 core/Config.php
View
@@ -173,12 +173,6 @@ public function pushConfigManifest(SS_ConfigManifest $manifest) {
$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 @@ function get($class, $name, $sourceOptions = 0, &$result = null, $suppress = nul
// 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);
5 core/Extension.php
View
@@ -46,14 +46,11 @@ function __construct() {
/**
* 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
}
/**
80 core/Object.php
View
@@ -463,6 +463,7 @@ public static function add_extension($class, $extension) {
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 @@ public static function remove_extension($class, $extension) {
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 @@ public static function get_extensions($class, $includeArgumentString = false) {
// -----------------------------------------------------------------------------------------------------------------
- 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);
-
- // 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;
- }
+ 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);
- $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;
25 model/DataExtension.php
View
@@ -31,34 +31,21 @@
'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');
}
7 model/Hierarchy.php
View
@@ -25,9 +25,10 @@ function augmentDatabase() {
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)
+ );
}
/**
7 model/Versioned.php
View
@@ -107,9 +107,10 @@ function __construct($stages=array('Stage','Live')) {
'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)
+ );
}
/**
18 search/FulltextSearchable.php
View
@@ -75,14 +75,16 @@ function __construct($searchFields = array()) {
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]
+ )
+ )
+ );
}
/**
Please sign in to comment.
Something went wrong with that request. Please try again.