mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API CHANGE: Modify extensions system to support new config system. Statics are now declared directly on extensions, and there is an add_to_class method extensions can hook into to modify class configuration
This commit is contained in:
parent
0ab171d7c0
commit
876f4c5299
@ -43,6 +43,19 @@ abstract class Extension {
|
||||
$this->class = get_class($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
Config::add_static_source($class, $extensionClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the owner of this extension.
|
||||
* @param Object $owner The owner object,
|
||||
@ -86,5 +99,7 @@ abstract class Extension {
|
||||
return (($p = strpos($extensionStr, '(')) !== false) ? substr($extensionStr, 0, $p) : $extensionStr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -422,7 +422,9 @@ abstract class Object {
|
||||
*/
|
||||
public static function has_extension($class, $requiredExtension) {
|
||||
$requiredExtension = strtolower($requiredExtension);
|
||||
if($extensions = self::combined_static($class, 'extensions')) foreach($extensions as $extension) {
|
||||
$extensions = Config::inst()->get($class, 'extensions');
|
||||
|
||||
if($extensions) foreach($extensions as $extension) {
|
||||
$left = strtolower(Extension::get_classname_without_arguments($extension));
|
||||
$right = strtolower(Extension::get_classname_without_arguments($requiredExtension));
|
||||
if($left == $right) return true;
|
||||
@ -457,30 +459,19 @@ abstract class Object {
|
||||
}
|
||||
|
||||
// unset some caches
|
||||
self::$cached_statics[$class]['extensions'] = null;
|
||||
$subclasses = ClassInfo::subclassesFor($class);
|
||||
$subclasses[] = $class;
|
||||
|
||||
if($subclasses) foreach($subclasses as $subclass) {
|
||||
unset(self::$classes_constructed[$subclass]);
|
||||
unset(self::$extra_methods[$subclass]);
|
||||
}
|
||||
|
||||
// merge with existing static vars
|
||||
$extensions = self::uninherited_static($class, 'extensions');
|
||||
|
||||
// We use unshift rather than push so that module extensions are added before built-in ones.
|
||||
// in particular, this ensures that the Versioned rewriting is done last.
|
||||
if($extensions) array_unshift($extensions, $extension);
|
||||
else $extensions = array($extension);
|
||||
|
||||
self::set_static($class, 'extensions', $extensions);
|
||||
Config::inst()->update($class, 'extensions', array($extension));
|
||||
|
||||
// load statics now for DataObject classes
|
||||
if(is_subclass_of($class, 'DataObject')) {
|
||||
if(is_subclass_of($extensionClass, 'DataExtension')) {
|
||||
DataExtension::load_extra_statics($class, $extension);
|
||||
}
|
||||
else {
|
||||
if(!is_subclass_of($extensionClass, 'DataExtension')) {
|
||||
user_error("$extensionClass cannot be applied to $class without being a DataExtension", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
@ -504,37 +495,19 @@ abstract class Object {
|
||||
* @param string $extension Classname of an {@link Extension} subclass, without parameters
|
||||
*/
|
||||
public static function remove_extension($class, $extension) {
|
||||
// unload statics now for DataObject classes
|
||||
if(is_subclass_of($class, 'DataObject')) {
|
||||
if(!preg_match('/^([^(]*)/', $extension, $matches)) {
|
||||
user_error("Bad extension '$extension'", E_USER_WARNING);
|
||||
} else {
|
||||
$extensionClass = $matches[1];
|
||||
DataExtension::unload_extra_statics($class, $extensionClass);
|
||||
}
|
||||
}
|
||||
|
||||
if(self::has_extension($class, $extension)) {
|
||||
self::set_static(
|
||||
$class,
|
||||
'extensions',
|
||||
array_diff(self::uninherited_static($class, 'extensions'), array($extension))
|
||||
);
|
||||
}
|
||||
Config::inst()->remove($class, 'extensions', Config::anything(), $extension);
|
||||
|
||||
// unset singletons to avoid side-effects
|
||||
global $_SINGLETONS;
|
||||
$_SINGLETONS = array();
|
||||
|
||||
// unset some caches
|
||||
self::$cached_statics[$class]['extensions'] = null;
|
||||
$subclasses = ClassInfo::subclassesFor($class);
|
||||
$subclasses[] = $class;
|
||||
if($subclasses) foreach($subclasses as $subclass) {
|
||||
unset(self::$classes_constructed[$subclass]);
|
||||
unset(self::$extra_methods[$subclass]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -545,7 +518,8 @@ abstract class Object {
|
||||
* or eval'ed classname strings with constructor arguments.
|
||||
*/
|
||||
function get_extensions($class, $includeArgumentString = false) {
|
||||
$extensions = self::get_static($class, 'extensions');
|
||||
$extensions = Config::inst()->get($class, 'extensions');
|
||||
|
||||
if($includeArgumentString) {
|
||||
return $extensions;
|
||||
} else {
|
||||
@ -559,6 +533,8 @@ abstract class Object {
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
private static $_added_extensions = array();
|
||||
|
||||
public function __construct() {
|
||||
$this->class = get_class($this);
|
||||
|
||||
@ -568,8 +544,21 @@ abstract class Object {
|
||||
if($extensionClasses = ClassInfo::ancestry($this->class)) foreach($extensionClasses as $class) {
|
||||
if(in_array($class, $notExtendable)) continue;
|
||||
|
||||
if($extensions = self::uninherited_static($class, 'extensions')) {
|
||||
if($extensions = Config::inst()->get($class, 'extensions', Config::UNINHERITED)) {
|
||||
foreach($extensions as $extension) {
|
||||
// Get the extension class for this extension
|
||||
$extensionClass = Extension::get_classname_without_arguments($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);
|
||||
|
||||
// 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;
|
||||
|
@ -31,58 +31,37 @@ abstract class DataExtension extends Extension {
|
||||
'api_access' => false,
|
||||
);
|
||||
|
||||
private static $extra_statics_loaded = array();
|
||||
|
||||
/**
|
||||
* Load the extra static definitions for the given extension
|
||||
* class name, called by {@link Object::add_extension()}
|
||||
*
|
||||
* @param string $class Class name of the owner class (or owner base class)
|
||||
* @param string $extension Class name of the extension class
|
||||
*/
|
||||
public static function load_extra_statics($class, $extension) {
|
||||
if(!empty(self::$extra_statics_loaded[$class][$extension])) return;
|
||||
self::$extra_statics_loaded[$class][$extension] = true;
|
||||
|
||||
if(preg_match('/^([^(]*)/', $extension, $matches)) {
|
||||
$extensionClass = $matches[1];
|
||||
static function add_to_class($class, $extensionClass) {
|
||||
if(method_exists($class, 'extraDBFields')) {
|
||||
$extraStaticsMethod = 'extraDBFields';
|
||||
} else {
|
||||
user_error("Bad extension '$extension' - can't find classname", E_USER_WARNING);
|
||||
return;
|
||||
$extraStaticsMethod = 'extraStatics';
|
||||
}
|
||||
|
||||
// If the extension has been manually applied to a subclass, we should ignore that.
|
||||
if(Object::has_extension(get_parent_class($class), $extensionClass)) return;
|
||||
$statics = singleton($extensionClass)->$extraStaticsMethod($class, $extensionClass);
|
||||
|
||||
// If there aren't any extraStatics we shouldn't try to load them.
|
||||
if(!method_exists($extensionClass, 'extraStatics')) return;
|
||||
if ($statics) {
|
||||
Deprecation::notice('3.0.0', "$extraStaticsMethod deprecated. Just define statics on your extension, or use add_to_class");
|
||||
|
||||
$statics = call_user_func(array(singleton($extensionClass), 'extraStatics'), $class, $extension);
|
||||
|
||||
if($statics) {
|
||||
foreach($statics as $name => $newVal) {
|
||||
if(isset(self::$extendable_statics[$name])) {
|
||||
|
||||
// Array to be merged
|
||||
if(self::$extendable_statics[$name]) {
|
||||
$origVal = Object::uninherited_static($class, $name);
|
||||
// Can't use add_static_var() here as it would merge the array rather than replacing
|
||||
Object::set_static($class, $name, array_merge((array)$origVal, $newVal));
|
||||
|
||||
// Value to be overwritten
|
||||
} else {
|
||||
Object::set_static($class, $name, $newVal);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
public static function unload_extra_statics($class, $extension) {
|
||||
self::$extra_statics_loaded[$class][$extension] = false;
|
||||
throw new Exception('unload_extra_statics gone');
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user