mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
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.
This commit is contained in:
parent
4916b361f1
commit
fa37c448a5
@ -231,6 +231,25 @@ class ClassInfo {
|
|||||||
|
|
||||||
return $matchedClasses;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,12 +173,6 @@ class Config {
|
|||||||
$this->collectConfigPHPSettings = false;
|
$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 */
|
/** @var [Config_ForClass] - The list of Config_ForClass instances, keyed off class */
|
||||||
static protected $for_class_instances = array();
|
static protected $for_class_instances = array();
|
||||||
|
|
||||||
@ -371,14 +365,17 @@ class Config {
|
|||||||
|
|
||||||
// Then look at the static variables
|
// Then look at the static variables
|
||||||
$nothing = new stdClass();
|
$nothing = new stdClass();
|
||||||
$classes = array($class);
|
|
||||||
|
$sources = array($class);
|
||||||
// Include extensions only if not flagged not to, and some have been set
|
// 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])) {
|
if (($sourceOptions & self::EXCLUDE_EXTRA_SOURCES) != self::EXCLUDE_EXTRA_SOURCES) {
|
||||||
$classes = array_merge($classes, self::$extra_static_sources[$class]);
|
$extraSources = Object::get_extra_config_sources($class);
|
||||||
|
if ($extraSources) $sources = array_merge($sources, $extraSources);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($classes as $staticSource) {
|
foreach ($sources as $staticSource) {
|
||||||
$value = Object::static_lookup($staticSource, $name, $nothing);
|
if (is_array($staticSource)) $value = isset($staticSource[$name]) ? $staticSource[$name] : $nothing;
|
||||||
|
else $value = Object::static_lookup($staticSource, $name, $nothing);
|
||||||
|
|
||||||
if ($value !== $nothing) {
|
if ($value !== $nothing) {
|
||||||
self::merge_low_into_high($result, $value, $suppress);
|
self::merge_low_into_high($result, $value, $suppress);
|
||||||
|
@ -46,14 +46,11 @@ abstract class Extension {
|
|||||||
/**
|
/**
|
||||||
* Called when this extension is added to a particular class
|
* 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
|
* @static
|
||||||
* @param $class
|
* @param $class
|
||||||
*/
|
*/
|
||||||
static function add_to_class($class, $extensionClass, $args = null) {
|
static function add_to_class($class, $extensionClass, $args = null) {
|
||||||
Config::add_static_source($class, $extensionClass);
|
// NOP
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -463,6 +463,7 @@ abstract class Object {
|
|||||||
if($subclasses) foreach($subclasses as $subclass) {
|
if($subclasses) foreach($subclasses as $subclass) {
|
||||||
unset(self::$classes_constructed[$subclass]);
|
unset(self::$classes_constructed[$subclass]);
|
||||||
unset(self::$extra_methods[$subclass]);
|
unset(self::$extra_methods[$subclass]);
|
||||||
|
unset(self::$extension_sources[$subclass]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::inst()->update($class, 'extensions', array($extension));
|
Config::inst()->update($class, 'extensions', array($extension));
|
||||||
@ -505,6 +506,7 @@ abstract class Object {
|
|||||||
if($subclasses) foreach($subclasses as $subclass) {
|
if($subclasses) foreach($subclasses as $subclass) {
|
||||||
unset(self::$classes_constructed[$subclass]);
|
unset(self::$classes_constructed[$subclass]);
|
||||||
unset(self::$extra_methods[$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() {
|
public function __construct() {
|
||||||
$this->class = get_class($this);
|
$this->class = get_class($this);
|
||||||
|
|
||||||
// Don't bother checking some classes that should never be extended
|
foreach(ClassInfo::ancestry(get_called_class()) as $class) {
|
||||||
static $notExtendable = array('Object', 'ViewableData', 'RequestHandler');
|
if(in_array($class, self::$unextendable_classes)) continue;
|
||||||
|
$extensions = Config::inst()->get($class, 'extensions', Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES);
|
||||||
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($extensions) foreach($extensions as $extension) {
|
||||||
if (!isset(self::$_added_extensions[$extensionClass][$class])) {
|
$instance = self::create_from_string($extension);
|
||||||
// First call the add_to_class method - this will inherit down & is defined on Extension, so if not defined, no worries
|
$instance->setOwner(null, $class);
|
||||||
call_user_func(array($extensionClass, 'add_to_class'), $class, $extensionClass, $extensionArgs);
|
$this->extension_instances[$instance->class] = $instance;
|
||||||
|
|
||||||
// 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(!isset(self::$classes_constructed[$this->class])) {
|
if(!isset(self::$classes_constructed[$this->class])) {
|
||||||
$this->defineMethods();
|
$this->defineMethods();
|
||||||
self::$classes_constructed[$this->class] = true;
|
self::$classes_constructed[$this->class] = true;
|
||||||
|
@ -31,34 +31,21 @@ abstract class DataExtension extends Extension {
|
|||||||
'api_access' => false,
|
'api_access' => false,
|
||||||
);
|
);
|
||||||
|
|
||||||
static function add_to_class($class, $extensionClass, $args = null) {
|
static function get_extra_config($class, $extension, $args) {
|
||||||
if(method_exists($class, 'extraDBFields')) {
|
if(method_exists($extension, 'extraDBFields')) {
|
||||||
$extraStaticsMethod = 'extraDBFields';
|
$extraStaticsMethod = 'extraDBFields';
|
||||||
} else {
|
} else {
|
||||||
$extraStaticsMethod = 'extraStatics';
|
$extraStaticsMethod = 'extraStatics';
|
||||||
}
|
}
|
||||||
|
|
||||||
$statics = Injector::inst()->get($extensionClass, true, $args)->$extraStaticsMethod($class, $extensionClass);
|
$statics = Injector::inst()->get($extension, true, $args)->$extraStaticsMethod();
|
||||||
|
|
||||||
if ($statics) {
|
if ($statics) {
|
||||||
Deprecation::notice('3.1.0', "$extraStaticsMethod deprecated. Just define statics on your extension, or use add_to_class", Deprecation::SCOPE_GLOBAL);
|
Deprecation::notice('3.1.0', "$extraStaticsMethod deprecated. Just define statics on your extension, or use get_extra_config", Deprecation::SCOPE_GLOBAL);
|
||||||
|
return $statics;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parent::add_to_class($class, $extensionClass, $args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function unload_extra_statics($class, $extension) {
|
public static function unload_extra_statics($class, $extension) {
|
||||||
throw new Exception('unload_extra_statics gone');
|
throw new Exception('unload_extra_statics gone');
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,10 @@ class Hierarchy extends DataExtension {
|
|||||||
function augmentWrite(&$manipulation) {
|
function augmentWrite(&$manipulation) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static function add_to_class($class, $extensionClass, $args = null) {
|
static function get_extra_config($class, $extension, $args) {
|
||||||
Config::inst()->update($class, 'has_one', array('Parent' => $class));
|
return array(
|
||||||
parent::add_to_class($class, $extensionClass, $args);
|
'has_one' => array('Parent' => $class)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,9 +107,10 @@ class Versioned extends DataExtension {
|
|||||||
'Version' => 'Int'
|
'Version' => 'Int'
|
||||||
);
|
);
|
||||||
|
|
||||||
static function add_to_class($class, $extensionClass, $args = null) {
|
static function get_extra_config($class, $extension, $args) {
|
||||||
Config::inst()->update($class, 'has_many', array('Versions' => $class));
|
array(
|
||||||
parent::add_to_class($class, $extensionClass, $args);
|
'has_many' => array('Versions' => $class)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,14 +75,16 @@ class FulltextSearchable extends DataExtension {
|
|||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
static function add_to_class($class, $extensionClass, $args = null) {
|
static function get_extra_config($class, $extensionClass, $args) {
|
||||||
Config::inst()->update($class, 'indexes', array('SearchFields' => array(
|
return array(
|
||||||
'type' => 'fulltext',
|
'indexes' => array(
|
||||||
'name' => 'SearchFields',
|
'SearchFields' => array(
|
||||||
'value' => $args[0]
|
'type' => 'fulltext',
|
||||||
)));
|
'name' => 'SearchFields',
|
||||||
|
'value' => $args[0]
|
||||||
parent::add_to_class($class, $extensionClass, $args);
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user