Merge pull request #734 from silverstripe-rebelalliance/trac/7799

API Reverse config extra statics control flow
This commit is contained in:
Sean Harvey 2012-08-26 14:24:05 -07:00
commit 674b52b4f7
8 changed files with 107 additions and 73 deletions

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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
}
/**

View File

@ -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;

View File

@ -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');
}

View File

@ -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)
);
}
/**

View File

@ -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)
);
}
/**

View File

@ -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]
)
)
);
}
/**