API CHANGE: Make Object::uninherited_static() have a separate execution path to Object::get_static(), for more reliable operation. The intention is that for any given static, you either use Object::get_static() or you use Object::uninherited_static() - not both.

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@84151 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Sam Minnee 2009-08-11 05:01:17 +00:00
parent 869625dcc5
commit d5cf940d69
4 changed files with 58 additions and 43 deletions

View File

@ -43,6 +43,8 @@ abstract class Object {
private static private static
$statics = array(), $statics = array(),
$cached_statics = array(), $cached_statics = array(),
$uninherited_statics = array(),
$cached_uninherited_statics = array(),
$extra_statics = array(), $extra_statics = array(),
$replaced_statics = array(), $replaced_statics = array(),
$_cache_statics_prepared = array(); $_cache_statics_prepared = array();
@ -245,7 +247,9 @@ abstract class Object {
} }
self::$statics[$class][$name] = $value; self::$statics[$class][$name] = $value;
self::$uninherited_statics[$class][$name] = $value;
self::$cached_statics[$class][$name] = true; self::$cached_statics[$class][$name] = true;
self::$cached_uninherited_statics[$class][$name] = true;
} }
/** /**
@ -260,25 +264,34 @@ abstract class Object {
* @param string $name * @param string $name
* @return mixed * @return mixed
*/ */
public static function uninherited_static($class, $name) { public static function uninherited_static($class, $name, $uncached = false) {
$inherited = self::get_static($class, $name); if(!isset(self::$_cache_statics_prepared[$class])) {
$parent = null; Object::prepare_statics($class);
if($parentClass = get_parent_class($class)) {
$parent = self::get_static($parentClass, $name);
} }
if(is_array($inherited) && is_array($parent)) { if(!isset(self::$cached_uninherited_statics[$class][$name]) || $uncached) {
return array_diff_assoc($inherited, $parent); $classRef = new ReflectionClass($class);
$classProps = $classRef->getStaticProperties();
$parentClass = get_parent_class($class);
if($parentClass) {
$parentRef = new ReflectionClass($parentClass);
$parentProps = $parentRef->getStaticProperties();
$uninheritedBuiltIn = isset($classProps[$name])
&& (!isset($parentProps[$name])
|| $classProps[$name] != $parentProps[$name]) ? $classProps[$name] : null;
} else {
$uninheritedBuiltIn = isset($classProps[$name]) ? $classProps[$name] : null;
} }
return ($inherited != $parent) ? $inherited : null;
self::$cached_uninherited_statics[$class][$name] = true;
self::$uninherited_statics[$class][$name] = $uninheritedBuiltIn;
} }
/** return self::$uninherited_statics[$class][$name];
* Cache the results of whether or not a given var is inherited. }
*/
private static $cache_is_inherited = array();
/** /**
* Traverse down a class ancestry and attempt to merge all the uninherited static values for a particular static * Traverse down a class ancestry and attempt to merge all the uninherited static values for a particular static
@ -361,7 +374,7 @@ 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::get_static($class, 'extensions')) foreach($extensions as $extension) { if($extensions = self::combined_static($class, '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;
@ -404,7 +417,9 @@ abstract class Object {
} }
// merge with existing static vars // merge with existing static vars
self::add_static_var($class, 'extensions', array($extension)); $extensions = self::uninherited_static($class, 'extensions');
$extensions[] = $extension;
self::set_static($class, 'extensions', $extensions);
// load statics now for DataObject classes // load statics now for DataObject classes
if(ClassInfo::is_subclass_of($class, 'DataObject')) { if(ClassInfo::is_subclass_of($class, 'DataObject')) {
@ -423,7 +438,7 @@ abstract class Object {
// load statics now for DataObject classes // load statics now for DataObject classes
if(is_subclass_of($class, 'DataObject')) { if(is_subclass_of($class, 'DataObject')) {
$extensions = Object::get_static($class, 'extensions'); $extensions = Object::uninherited_static($class, 'extensions');
if($extensions) foreach($extensions as $extension) { if($extensions) foreach($extensions as $extension) {
if(preg_match('/^([^(]*)/', $extension, $matches)) { if(preg_match('/^([^(]*)/', $extension, $matches)) {
$extensionClass = $matches[1]; $extensionClass = $matches[1];
@ -455,7 +470,7 @@ abstract class Object {
self::set_static( self::set_static(
$class, $class,
'extensions', 'extensions',
array_diff(self::get_static($class, 'extensions'), array($extension)) array_diff(self::uninherited_static($class, 'extensions'), array($extension))
); );
} }

View File

@ -810,7 +810,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$classes = array_reverse(ClassInfo::ancestry($this)); $classes = array_reverse(ClassInfo::ancestry($this));
foreach($classes as $class) { foreach($classes as $class) {
$defaults = Object::get_static($class, 'defaults'); $defaults = Object::uninherited_static($class, 'defaults');
if($defaults && !is_array($defaults)) { if($defaults && !is_array($defaults)) {
user_error("Bad '$this->class' defaults given: " . var_export($defaults, true), user_error("Bad '$this->class' defaults given: " . var_export($defaults, true),
@ -1426,13 +1426,13 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if(in_array($class, array('Object', 'ViewableData', 'DataObject'))) continue; if(in_array($class, array('Object', 'ViewableData', 'DataObject'))) continue;
if($component) { if($component) {
$hasOne = Object::get_static($class, 'has_one'); $hasOne = Object::uninherited_static($class, 'has_one');
if(isset($hasOne[$component])) { if(isset($hasOne[$component])) {
return $hasOne[$component]; return $hasOne[$component];
} }
} else { } else {
$newItems = (array) Object::get_static($class, 'has_one'); $newItems = (array) Object::uninherited_static($class, 'has_one');
// Validate the data // Validate the data
foreach($newItems as $k => $v) { foreach($newItems as $k => $v) {
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$has_one has a bad entry: " if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$has_one has a bad entry: "
@ -1466,13 +1466,13 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
} }
if($fieldName) { if($fieldName) {
$db = Object::get_static($class, 'db'); $db = Object::uninherited_static($class, 'db');
if(isset($db[$fieldName])) { if(isset($db[$fieldName])) {
return $db[$fieldName]; return $db[$fieldName];
} }
} else { } else {
$newItems = (array) Object::get_static($class, 'db'); $newItems = (array) Object::uninherited_static($class, 'db');
// Validate the data // Validate the data
foreach($newItems as $k => $v) { foreach($newItems as $k => $v) {
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$db has a bad entry: " if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$db has a bad entry: "
@ -1500,13 +1500,13 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if(in_array($class, array('ViewableData', 'Object', 'DataObject'))) continue; if(in_array($class, array('ViewableData', 'Object', 'DataObject'))) continue;
if($component) { if($component) {
$hasMany = Object::get_static($class, 'has_many'); $hasMany = Object::uninherited_static($class, 'has_many');
if(isset($hasMany[$component])) { if(isset($hasMany[$component])) {
return $hasMany[$component]; return $hasMany[$component];
} }
} else { } else {
$newItems = (array) Object::get_static($class, 'has_many'); $newItems = (array) Object::uninherited_static($class, 'has_many');
// Validate the data // Validate the data
foreach($newItems as $k => $v) { foreach($newItems as $k => $v) {
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$has_many has a bad entry: " if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$has_many has a bad entry: "
@ -1620,7 +1620,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if(in_array($class, array('ViewableData', 'Object', 'DataObject'))) continue; if(in_array($class, array('ViewableData', 'Object', 'DataObject'))) continue;
if($component) { if($component) {
$manyMany = Object::get_static($class, 'many_many'); $manyMany = Object::uninherited_static($class, 'many_many');
// Try many_many // Try many_many
$candidate = (isset($manyMany[$component])) ? $manyMany[$component] : null; $candidate = (isset($manyMany[$component])) ? $manyMany[$component] : null;
if($candidate) { if($candidate) {
@ -1630,13 +1630,13 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
} }
// Try belongs_many_many // Try belongs_many_many
$belongsManyMany = Object::get_static($class, 'belongs_many_many'); $belongsManyMany = Object::uninherited_static($class, 'belongs_many_many');
$candidate = (isset($belongsManyMany[$component])) ? $belongsManyMany[$component] : null; $candidate = (isset($belongsManyMany[$component])) ? $belongsManyMany[$component] : null;
if($candidate) { if($candidate) {
$childField = $candidate . "ID"; $childField = $candidate . "ID";
// We need to find the inverse component name // We need to find the inverse component name
$otherManyMany = Object::get_static($candidate, 'many_many'); $otherManyMany = Object::uninherited_static($candidate, 'many_many');
if(!$otherManyMany) { if(!$otherManyMany) {
user_error("Inverse component of $candidate not found ({$this->class})", E_USER_ERROR); user_error("Inverse component of $candidate not found ({$this->class})", E_USER_ERROR);
} }
@ -1655,7 +1655,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
user_error("Orphaned \$belongs_many_many value for $this->class.$component", E_USER_ERROR); user_error("Orphaned \$belongs_many_many value for $this->class.$component", E_USER_ERROR);
} }
} else { } else {
$newItems = (array) Object::get_static($class, 'many_many'); $newItems = (array) Object::uninherited_static($class, 'many_many');
// Validate the data // Validate the data
foreach($newItems as $k => $v) { foreach($newItems as $k => $v) {
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$many_many has a bad entry: " if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$many_many has a bad entry: "
@ -1663,7 +1663,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
} }
$items = isset($items) ? array_merge($newItems, $items) : $newItems; $items = isset($items) ? array_merge($newItems, $items) : $newItems;
$newItems = (array) Object::get_static($class, 'belongs_many_many'); $newItems = (array) Object::uninherited_static($class, 'belongs_many_many');
// Validate the data // Validate the data
foreach($newItems as $k => $v) { foreach($newItems as $k => $v) {
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$belongs_many_many has a bad entry: " if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$belongs_many_many has a bad entry: "

View File

@ -63,10 +63,10 @@ abstract class DataObjectDecorator extends Extension {
if($statics) { if($statics) {
foreach($statics as $name => $newVal) { foreach($statics as $name => $newVal) {
if(isset(self::$decoratable_statics[$name])) { if(isset(self::$decoratable_statics[$name])) {
$origVal = self::get_static($class, $name);
// Array to be merged // Array to be merged
if(self::$decoratable_statics[$name]) { if(self::$decoratable_statics[$name]) {
$origVal = self::uninherited_static($class, $name);
// Can't use add_static_var() here as it would merge the array rather than replacing // Can't use add_static_var() here as it would merge the array rather than replacing
self::set_static($class, $name, array_merge((array)$origVal, $newVal)); self::set_static($class, $name, array_merge((array)$origVal, $newVal));

View File

@ -70,14 +70,14 @@ class ObjectStaticTest extends SapphireTest {
} }
/** /**
* Check that calls to Object::add_static() will update the data returned by Object::uninherited_static() * Confirms that Object::add_static_var() doesn't work for uninherited stats
*/ */
public function testAddStaticFollowedByUnheritedCall() { public function testAddStaticVarDoesntWorkFor() {
Object::add_static_var('ObjectStaticTest_First', 'first', array('test_1b')); Object::add_static_var('ObjectStaticTest_First', 'first', array('test_1b'));
Object::add_static_var('ObjectStaticTest_Second', 'first', array('test_2b')); Object::add_static_var('ObjectStaticTest_Second', 'first', array('test_2b'));
$this->assertContains('test_1b', Object::uninherited_static('ObjectStaticTest_First', 'first')); $this->assertNotContains('test_1b', Object::uninherited_static('ObjectStaticTest_First', 'first'));
$this->assertContains('test_2b', Object::uninherited_static('ObjectStaticTest_Second', 'first')); $this->assertNotContains('test_2b', Object::uninherited_static('ObjectStaticTest_Second', 'first'));
} }
} }