mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Ensuring classinfo is case insensitive
This commit is contained in:
parent
5f0d0ab66a
commit
ffbeac6b7d
@ -26,7 +26,7 @@ class SilverStripeServiceConfigurationLocator extends ServiceConfigurationLocato
|
||||
|
||||
// do parent lookup if it's a class
|
||||
if (class_exists($name)) {
|
||||
$parents = array_reverse(array_keys(ClassInfo::ancestry($name)));
|
||||
$parents = array_reverse(array_values(ClassInfo::ancestry($name)));
|
||||
array_shift($parents);
|
||||
|
||||
foreach ($parents as $parent) {
|
||||
|
@ -63,6 +63,7 @@ class ClassInfo {
|
||||
* @param string $class Class name to check enum values for ClassName field
|
||||
*/
|
||||
public static function getValidSubClasses($class = 'SiteTree', $includeUnbacked = false) {
|
||||
$class = self::class_name($class);
|
||||
$classes = DB::getConn()->enumValuesForField($class, 'ClassName');
|
||||
if (!$includeUnbacked) $classes = array_filter($classes, array('ClassInfo', 'exists'));
|
||||
return $classes;
|
||||
@ -79,9 +80,7 @@ class ClassInfo {
|
||||
public static function dataClassesFor($class) {
|
||||
$result = array();
|
||||
|
||||
if (is_object($class)) {
|
||||
$class = get_class($class);
|
||||
}
|
||||
$class = self::class_name($class);
|
||||
|
||||
$classes = array_merge(
|
||||
self::ancestry($class),
|
||||
@ -102,7 +101,7 @@ class ClassInfo {
|
||||
* @return string
|
||||
*/
|
||||
public static function baseDataClass($class) {
|
||||
if (is_object($class)) $class = get_class($class);
|
||||
$class = self::class_name($class);
|
||||
|
||||
if (!is_subclass_of($class, 'DataObject')) {
|
||||
throw new InvalidArgumentException("$class is not a subclass of DataObject");
|
||||
@ -126,7 +125,7 @@ class ClassInfo {
|
||||
* <code>
|
||||
* ClassInfo::subclassesFor('BaseClass');
|
||||
* array(
|
||||
* 0 => 'BaseClass',
|
||||
* 'BaseClass' => 'BaseClass',
|
||||
* 'ChildClass' => 'ChildClass',
|
||||
* 'GrandChildClass' => 'GrandChildClass'
|
||||
* )
|
||||
@ -136,8 +135,10 @@ class ClassInfo {
|
||||
* @return array Names of all subclasses as an associative array.
|
||||
*/
|
||||
public static function subclassesFor($class) {
|
||||
//normalise class case
|
||||
$className = self::class_name($class);
|
||||
$descendants = SS_ClassLoader::instance()->getManifest()->getDescendantsOf($class);
|
||||
$result = array($class => $class);
|
||||
$result = array($className => $className);
|
||||
|
||||
if ($descendants) {
|
||||
return $result + ArrayLib::valuekey($descendants);
|
||||
@ -146,6 +147,23 @@ class ClassInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a class name in any case and return it as it was defined in PHP
|
||||
*
|
||||
* eg: self::class_name('dataobJEct'); //returns 'DataObject'
|
||||
*
|
||||
* @param string|object $nameOrObject The classname or object you want to normalise
|
||||
*
|
||||
* @return string The normalised class name
|
||||
*/
|
||||
public static function class_name($nameOrObject) {
|
||||
if (is_object($nameOrObject)) {
|
||||
return get_class($nameOrObject);
|
||||
}
|
||||
$reflection = new ReflectionClass($nameOrObject);
|
||||
return $reflection->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the passed class name along with all its parent class names in an
|
||||
* array, sorted with the root class first.
|
||||
@ -155,9 +173,11 @@ class ClassInfo {
|
||||
* @return array
|
||||
*/
|
||||
public static function ancestry($class, $tablesOnly = false) {
|
||||
if (!is_string($class)) $class = get_class($class);
|
||||
$class = self::class_name($class);
|
||||
|
||||
$cacheKey = $class . '_' . (string)$tablesOnly;
|
||||
$lClass = strtolower($class);
|
||||
|
||||
$cacheKey = $lClass . '_' . (string)$tablesOnly;
|
||||
$parent = $class;
|
||||
if(!isset(self::$_cache_ancestry[$cacheKey])) {
|
||||
$ancestry = array();
|
||||
@ -184,7 +204,7 @@ class ClassInfo {
|
||||
* Returns true if the given class implements the given interface
|
||||
*/
|
||||
public static function classImplements($className, $interfaceName) {
|
||||
return in_array($className, SS_ClassLoader::instance()->getManifest()->getImplementorsOf($interfaceName));
|
||||
return in_array($className, self::implementorsOf($interfaceName));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -233,20 +253,23 @@ class ClassInfo {
|
||||
private static $method_from_cache = array();
|
||||
|
||||
public static function has_method_from($class, $method, $compclass) {
|
||||
if (!isset(self::$method_from_cache[$class])) self::$method_from_cache[$class] = array();
|
||||
$lClass = strtolower($class);
|
||||
$lMethod = strtolower($method);
|
||||
$lCompclass = strtolower($compclass);
|
||||
if (!isset(self::$method_from_cache[$lClass])) self::$method_from_cache[$lClass] = array();
|
||||
|
||||
if (!array_key_exists($method, self::$method_from_cache[$class])) {
|
||||
self::$method_from_cache[$class][$method] = false;
|
||||
if (!array_key_exists($lMethod, self::$method_from_cache[$lClass])) {
|
||||
self::$method_from_cache[$lClass][$lMethod] = false;
|
||||
|
||||
$classRef = new ReflectionClass($class);
|
||||
|
||||
if ($classRef->hasMethod($method)) {
|
||||
$methodRef = $classRef->getMethod($method);
|
||||
self::$method_from_cache[$class][$method] = $methodRef->getDeclaringClass()->getName();
|
||||
self::$method_from_cache[$lClass][$lMethod] = $methodRef->getDeclaringClass()->getName();
|
||||
}
|
||||
}
|
||||
|
||||
return self::$method_from_cache[$class][$method] == $compclass;
|
||||
return strtolower(self::$method_from_cache[$lClass][$lMethod]) == $lCompclass;
|
||||
}
|
||||
|
||||
|
||||
@ -261,11 +284,14 @@ class ClassInfo {
|
||||
* @return string
|
||||
*/
|
||||
public static function table_for_object_field($candidateClass, $fieldName) {
|
||||
if(!$candidateClass || !$fieldName) {
|
||||
if(!$candidateClass || !$fieldName || !is_subclass_of($candidateClass, 'DataObject')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$exists = class_exists($candidateClass);
|
||||
//normalise class name
|
||||
$candidateClass = self::class_name($candidateClass);
|
||||
|
||||
$exists = self::exists($candidateClass);
|
||||
|
||||
while($candidateClass && $candidateClass != 'DataObject' && $exists) {
|
||||
if(DataObject::has_own_table($candidateClass)) {
|
||||
@ -277,7 +303,7 @@ class ClassInfo {
|
||||
}
|
||||
|
||||
$candidateClass = get_parent_class($candidateClass);
|
||||
$exists = class_exists($candidateClass);
|
||||
$exists = $candidateClass && self::exists($candidateClass);
|
||||
}
|
||||
|
||||
if(!$candidateClass || !$exists) {
|
||||
|
@ -2441,8 +2441,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
return 'Int';
|
||||
}
|
||||
// get cached fieldmap
|
||||
$fieldMap = isset(DataObject::$cache_has_own_table_field[$this->class])
|
||||
? DataObject::$cache_has_own_table_field[$this->class] : null;
|
||||
$lClass = strtolower($this->class);
|
||||
$fieldMap = isset(DataObject::$cache_has_own_table_field[$lClass])
|
||||
? DataObject::$cache_has_own_table_field[$lClass] : null;
|
||||
|
||||
// if no fieldmap is cached, get all fields
|
||||
if(!$fieldMap) {
|
||||
@ -2464,7 +2465,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
}
|
||||
|
||||
// set cached fieldmap
|
||||
DataObject::$cache_has_own_table_field[$this->class] = $fieldMap;
|
||||
DataObject::$cache_has_own_table_field[$lClass] = $fieldMap;
|
||||
}
|
||||
|
||||
// Remove string-based "constructor-arguments" from the DBField definition
|
||||
@ -2482,18 +2483,18 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_own_table($dataClass) {
|
||||
if(!is_subclass_of($dataClass,'DataObject')) return false;
|
||||
if(!is_subclass_of($dataClass, 'DataObject')) return false;
|
||||
$lDataClass = strtolower($dataClass);
|
||||
|
||||
if(!isset(DataObject::$cache_has_own_table[$dataClass])) {
|
||||
if(get_parent_class($dataClass) == 'DataObject') {
|
||||
DataObject::$cache_has_own_table[$dataClass] = true;
|
||||
if(!isset(DataObject::$cache_has_own_table[$lDataClass])) {
|
||||
if(get_parent_class($dataClass) == 'DataObject' || Config::inst()->get($dataClass, 'db', Config::UNINHERITED)
|
||||
|| Config::inst()->get($dataClass, 'has_one', Config::UNINHERITED)) {
|
||||
DataObject::$cache_has_own_table[$lDataClass] = $dataClass;
|
||||
} else {
|
||||
DataObject::$cache_has_own_table[$dataClass]
|
||||
= Config::inst()->get($dataClass, 'db', Config::UNINHERITED)
|
||||
|| Config::inst()->get($dataClass, 'has_one', Config::UNINHERITED);
|
||||
DataObject::$cache_has_own_table[$lDataClass] = false;
|
||||
}
|
||||
}
|
||||
return DataObject::$cache_has_own_table[$dataClass];
|
||||
return (bool)DataObject::$cache_has_own_table[$lDataClass];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -169,7 +169,7 @@ class DataQuery {
|
||||
$tableClasses = $ancestorTables;
|
||||
}
|
||||
|
||||
$tableNames = array_keys($tableClasses);
|
||||
$tableNames = array_values($tableClasses);
|
||||
$baseClass = $tableNames[0];
|
||||
|
||||
// Iterate over the tables and check what we need to select from them. If any selects are made (or the table is
|
||||
|
@ -6,10 +6,18 @@
|
||||
*/
|
||||
class ClassInfoTest extends SapphireTest {
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
ClassInfo::reset_db_cache();
|
||||
}
|
||||
|
||||
public function testExists() {
|
||||
$this->assertTrue(ClassInfo::exists('Object'));
|
||||
$this->assertTrue(ClassInfo::exists('object'));
|
||||
$this->assertTrue(ClassInfo::exists('ClassInfoTest'));
|
||||
$this->assertTrue(ClassInfo::exists('CLASSINFOTEST'));
|
||||
$this->assertTrue(ClassInfo::exists('stdClass'));
|
||||
$this->assertTrue(ClassInfo::exists('stdCLASS'));
|
||||
}
|
||||
|
||||
public function testSubclassesFor() {
|
||||
@ -22,6 +30,16 @@ class ClassInfoTest extends SapphireTest {
|
||||
),
|
||||
'ClassInfo::subclassesFor() returns only direct subclasses and doesnt include base class'
|
||||
);
|
||||
ClassInfo::reset_db_cache();
|
||||
$this->assertEquals(
|
||||
ClassInfo::subclassesFor('classinfotest_baseclass'),
|
||||
array(
|
||||
'ClassInfoTest_BaseClass' => 'ClassInfoTest_BaseClass',
|
||||
'ClassInfoTest_ChildClass' => 'ClassInfoTest_ChildClass',
|
||||
'ClassInfoTest_GrandChildClass' => 'ClassInfoTest_GrandChildClass'
|
||||
),
|
||||
'ClassInfo::subclassesFor() is acting in a case sensitive way when it should not'
|
||||
);
|
||||
}
|
||||
|
||||
public function testClassesForFolder() {
|
||||
@ -34,11 +52,11 @@ class ClassInfoTest extends SapphireTest {
|
||||
$classes,
|
||||
'ClassInfo::classes_for_folder() returns classes matching the filename'
|
||||
);
|
||||
// $this->assertContains(
|
||||
// 'ClassInfoTest_BaseClass',
|
||||
// $classes,
|
||||
// 'ClassInfo::classes_for_folder() returns additional classes not matching the filename'
|
||||
// );
|
||||
$this->assertContains(
|
||||
'classinfotest_baseclass',
|
||||
$classes,
|
||||
'ClassInfo::classes_for_folder() returns additional classes not matching the filename'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -46,8 +64,11 @@ class ClassInfoTest extends SapphireTest {
|
||||
*/
|
||||
public function testBaseDataClass() {
|
||||
$this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('ClassInfoTest_BaseClass'));
|
||||
$this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('classinfotest_baseclass'));
|
||||
$this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('ClassInfoTest_ChildClass'));
|
||||
$this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('CLASSINFOTEST_CHILDCLASS'));
|
||||
$this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('ClassInfoTest_GrandChildClass'));
|
||||
$this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('ClassInfoTest_GRANDChildClass'));
|
||||
|
||||
$this->setExpectedException('InvalidArgumentException');
|
||||
ClassInfo::baseDataClass('DataObject');
|
||||
@ -67,6 +88,13 @@ class ClassInfoTest extends SapphireTest {
|
||||
));
|
||||
$this->assertEquals($expect, $ancestry);
|
||||
|
||||
ClassInfo::reset_db_cache();
|
||||
$this->assertEquals($expect, ClassInfo::ancestry('classINFOTest_Childclass'));
|
||||
|
||||
ClassInfo::reset_db_cache();
|
||||
$this->assertEquals($expect, ClassInfo::ancestry('classINFOTest_Childclass'));
|
||||
|
||||
ClassInfo::reset_db_cache();
|
||||
$ancestry = ClassInfo::ancestry('ClassInfoTest_ChildClass', true);
|
||||
$this->assertEquals(array('ClassInfoTest_BaseClass' => 'ClassInfoTest_BaseClass'), $ancestry,
|
||||
'$tablesOnly option excludes memory-only inheritance classes'
|
||||
@ -89,8 +117,11 @@ class ClassInfoTest extends SapphireTest {
|
||||
'ClassInfoTest_HasFields',
|
||||
);
|
||||
|
||||
|
||||
ClassInfo::reset_db_cache();
|
||||
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[0]));
|
||||
ClassInfo::reset_db_cache();
|
||||
$this->assertEquals($expect, ClassInfo::dataClassesFor(strtoupper($classes[0])));
|
||||
ClassInfo::reset_db_cache();
|
||||
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[1]));
|
||||
|
||||
$expect = array(
|
||||
@ -98,7 +129,10 @@ class ClassInfoTest extends SapphireTest {
|
||||
'ClassInfoTest_HasFields' => 'ClassInfoTest_HasFields',
|
||||
);
|
||||
|
||||
ClassInfo::reset_db_cache();
|
||||
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[2]));
|
||||
ClassInfo::reset_db_cache();
|
||||
$this->assertEquals($expect, ClassInfo::dataClassesFor(strtolower($classes[2])));
|
||||
}
|
||||
|
||||
public function testTableForObjectField() {
|
||||
@ -106,6 +140,10 @@ class ClassInfoTest extends SapphireTest {
|
||||
ClassInfo::table_for_object_field('ClassInfoTest_WithRelation', 'RelationID')
|
||||
);
|
||||
|
||||
$this->assertEquals('ClassInfoTest_WithRelation',
|
||||
ClassInfo::table_for_object_field('ClassInfoTest_withrelation', 'RelationID')
|
||||
);
|
||||
|
||||
$this->assertEquals('ClassInfoTest_BaseDataClass',
|
||||
ClassInfo::table_for_object_field('ClassInfoTest_BaseDataClass', 'Title')
|
||||
);
|
||||
@ -118,6 +156,10 @@ class ClassInfoTest extends SapphireTest {
|
||||
ClassInfo::table_for_object_field('ClassInfoTest_NoFields', 'Title')
|
||||
);
|
||||
|
||||
$this->assertEquals('ClassInfoTest_BaseDataClass',
|
||||
ClassInfo::table_for_object_field('classinfotest_nofields', 'Title')
|
||||
);
|
||||
|
||||
$this->assertEquals('ClassInfoTest_HasFields',
|
||||
ClassInfo::table_for_object_field('ClassInfoTest_HasFields', 'Description')
|
||||
);
|
||||
|
@ -138,6 +138,11 @@ class DataListTest extends SapphireTest {
|
||||
$this->assertEquals('DataObjectTest_TeamComment',$list->dataClass());
|
||||
}
|
||||
|
||||
public function testDataClassCaseInsensitive() {
|
||||
$list = DataList::create('dataobjecttest_teamcomment');
|
||||
$this->assertTrue($list->exists());
|
||||
}
|
||||
|
||||
public function testClone() {
|
||||
$list = DataObjectTest_TeamComment::get();
|
||||
$this->assertEquals($list, clone($list));
|
||||
|
Loading…
x
Reference in New Issue
Block a user