mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00: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);
|
$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.
|
* Set the owner of this extension.
|
||||||
* @param Object $owner The owner object,
|
* @param Object $owner The owner object,
|
||||||
@ -85,6 +98,8 @@ abstract class Extension {
|
|||||||
public static function get_classname_without_arguments($extensionStr) {
|
public static function get_classname_without_arguments($extensionStr) {
|
||||||
return (($p = strpos($extensionStr, '(')) !== false) ? substr($extensionStr, 0, $p) : $extensionStr;
|
return (($p = strpos($extensionStr, '(')) !== false) ? substr($extensionStr, 0, $p) : $extensionStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,7 +422,9 @@ abstract class Object {
|
|||||||
*/
|
*/
|
||||||
public static function has_extension($class, $requiredExtension) {
|
public static function has_extension($class, $requiredExtension) {
|
||||||
$requiredExtension = strtolower($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));
|
$left = strtolower(Extension::get_classname_without_arguments($extension));
|
||||||
$right = strtolower(Extension::get_classname_without_arguments($requiredExtension));
|
$right = strtolower(Extension::get_classname_without_arguments($requiredExtension));
|
||||||
if($left == $right) return true;
|
if($left == $right) return true;
|
||||||
@ -457,30 +459,19 @@ abstract class Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// unset some caches
|
// unset some caches
|
||||||
self::$cached_statics[$class]['extensions'] = null;
|
|
||||||
$subclasses = ClassInfo::subclassesFor($class);
|
$subclasses = ClassInfo::subclassesFor($class);
|
||||||
$subclasses[] = $class;
|
$subclasses[] = $class;
|
||||||
|
|
||||||
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge with existing static vars
|
Config::inst()->update($class, 'extensions', array($extension));
|
||||||
$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);
|
|
||||||
|
|
||||||
// load statics now for DataObject classes
|
// load statics now for DataObject classes
|
||||||
if(is_subclass_of($class, 'DataObject')) {
|
if(is_subclass_of($class, 'DataObject')) {
|
||||||
if(is_subclass_of($extensionClass, 'DataExtension')) {
|
if(!is_subclass_of($extensionClass, 'DataExtension')) {
|
||||||
DataExtension::load_extra_statics($class, $extension);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
user_error("$extensionClass cannot be applied to $class without being a DataExtension", E_USER_ERROR);
|
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
|
* @param string $extension Classname of an {@link Extension} subclass, without parameters
|
||||||
*/
|
*/
|
||||||
public static function remove_extension($class, $extension) {
|
public static function remove_extension($class, $extension) {
|
||||||
// unload statics now for DataObject classes
|
Config::inst()->remove($class, 'extensions', Config::anything(), $extension);
|
||||||
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))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// unset singletons to avoid side-effects
|
// unset singletons to avoid side-effects
|
||||||
global $_SINGLETONS;
|
global $_SINGLETONS;
|
||||||
$_SINGLETONS = array();
|
$_SINGLETONS = array();
|
||||||
|
|
||||||
// unset some caches
|
// unset some caches
|
||||||
self::$cached_statics[$class]['extensions'] = null;
|
|
||||||
$subclasses = ClassInfo::subclassesFor($class);
|
$subclasses = ClassInfo::subclassesFor($class);
|
||||||
$subclasses[] = $class;
|
$subclasses[] = $class;
|
||||||
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -545,7 +518,8 @@ abstract class Object {
|
|||||||
* or eval'ed classname strings with constructor arguments.
|
* or eval'ed classname strings with constructor arguments.
|
||||||
*/
|
*/
|
||||||
function get_extensions($class, $includeArgumentString = false) {
|
function get_extensions($class, $includeArgumentString = false) {
|
||||||
$extensions = self::get_static($class, 'extensions');
|
$extensions = Config::inst()->get($class, 'extensions');
|
||||||
|
|
||||||
if($includeArgumentString) {
|
if($includeArgumentString) {
|
||||||
return $extensions;
|
return $extensions;
|
||||||
} else {
|
} else {
|
||||||
@ -558,7 +532,9 @@ abstract class Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static $_added_extensions = array();
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->class = get_class($this);
|
$this->class = get_class($this);
|
||||||
|
|
||||||
@ -567,9 +543,22 @@ abstract class Object {
|
|||||||
|
|
||||||
if($extensionClasses = ClassInfo::ancestry($this->class)) foreach($extensionClasses as $class) {
|
if($extensionClasses = ClassInfo::ancestry($this->class)) foreach($extensionClasses as $class) {
|
||||||
if(in_array($class, $notExtendable)) continue;
|
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) {
|
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 = self::create_from_string($extension);
|
||||||
$instance->setOwner(null, $class);
|
$instance->setOwner(null, $class);
|
||||||
$this->extension_instances[$instance->class] = $instance;
|
$this->extension_instances[$instance->class] = $instance;
|
||||||
|
@ -30,59 +30,38 @@ abstract class DataExtension extends Extension {
|
|||||||
'searchable_fields' => true,
|
'searchable_fields' => true,
|
||||||
'api_access' => false,
|
'api_access' => false,
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $extra_statics_loaded = array();
|
|
||||||
|
static function add_to_class($class, $extensionClass) {
|
||||||
/**
|
if(method_exists($class, 'extraDBFields')) {
|
||||||
* Load the extra static definitions for the given extension
|
$extraStaticsMethod = 'extraDBFields';
|
||||||
* 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];
|
|
||||||
} else {
|
} else {
|
||||||
user_error("Bad extension '$extension' - can't find classname", E_USER_WARNING);
|
$extraStaticsMethod = 'extraStatics';
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the extension has been manually applied to a subclass, we should ignore that.
|
|
||||||
if(Object::has_extension(get_parent_class($class), $extensionClass)) return;
|
|
||||||
|
|
||||||
// If there aren't any extraStatics we shouldn't try to load them.
|
$statics = singleton($extensionClass)->$extraStaticsMethod($class, $extensionClass);
|
||||||
if(!method_exists($extensionClass, 'extraStatics')) return;
|
|
||||||
|
|
||||||
$statics = call_user_func(array(singleton($extensionClass), 'extraStatics'), $class, $extension);
|
if ($statics) {
|
||||||
|
Deprecation::notice('3.0.0', "$extraStaticsMethod deprecated. Just define statics on your extension, or use add_to_class");
|
||||||
if($statics) {
|
|
||||||
foreach($statics as $name => $newVal) {
|
// TODO: This currently makes extraStatics the MOST IMPORTANT config layer, not the least
|
||||||
if(isset(self::$extendable_statics[$name])) {
|
foreach (self::$extendable_statics as $key => $merge) {
|
||||||
|
if (isset($statics[$key])) {
|
||||||
// Array to be merged
|
if (!$merge) Config::inst()->remove($class, $key);
|
||||||
if(self::$extendable_statics[$name]) {
|
Config::inst()->update($class, $key, $statics[$key]);
|
||||||
$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 - remove this
|
||||||
DataObject::$cache_has_own_table[$class] = null;
|
DataObject::$cache_has_own_table[$class] = null;
|
||||||
DataObject::$cache_has_own_table_field[$class] = null;
|
DataObject::$cache_has_own_table_field[$class] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent::add_to_class($class, $extensionClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function unload_extra_statics($class, $extension) {
|
public static function unload_extra_statics($class, $extension) {
|
||||||
self::$extra_statics_loaded[$class][$extension] = false;
|
throw new Exception('unload_extra_statics gone');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user