ENHANCEMENT Don't force all class names to lowercase

Speeds up autoloading because composer psr-4 works properly now
This commit is contained in:
Damian Mooyman 2017-09-19 16:55:39 +12:00
parent 9a7adc46f8
commit 261302a121
No known key found for this signature in database
GPG Key ID: 78B823A10DE27D1A
16 changed files with 581 additions and 348 deletions

View File

@ -116,7 +116,7 @@ class ContentNegotiator
$chosenFormat = "xhtml";
} else {
foreach ($mimes as $format => $mime) {
$regExp = '/' . str_replace(array('+','/'), array('\+','\/'), $mime) . '(;q=(\d+\.\d+))?/i';
$regExp = '/' . str_replace(array('+', '/'), array('\+', '\/'), $mime) . '(;q=(\d+\.\d+))?/i';
if (isset($_SERVER['HTTP_ACCEPT']) && preg_match($regExp, $_SERVER['HTTP_ACCEPT'], $matches)) {
$preference = isset($matches[2]) ? $matches[2] : 1;
if (!isset($q[$preference])) {
@ -130,13 +130,13 @@ class ContentNegotiator
krsort($q);
$chosenFormat = reset($q);
} else {
$chosenFormat = Config::inst()->get('SilverStripe\\Control\\ContentNegotiator', 'default_format');
$chosenFormat = Config::inst()->get(static::class, 'default_format');
}
}
}
$negotiator = new ContentNegotiator();
$negotiator->$chosenFormat( $response );
$negotiator->$chosenFormat($response);
}
/**
@ -202,7 +202,7 @@ class ContentNegotiator
$response->addHeader("Vary", "Accept");
$content = $response->getBody();
$hasXMLHeader = (substr($content, 0, 5) == '<' . '?xml' );
$hasXMLHeader = (substr($content, 0, 5) == '<' . '?xml');
// Fix base tag
$content = preg_replace(
@ -212,7 +212,11 @@ class ContentNegotiator
);
$content = preg_replace("#<\\?xml[^>]+\\?>\n?#", '', $content);
$content = str_replace(array('/>','xml:lang','application/xhtml+xml'), array('>','lang','text/html'), $content);
$content = str_replace(
array('/>', 'xml:lang', 'application/xhtml+xml'),
array('>', 'lang', 'text/html'),
$content
);
// Only replace the doctype in templates with the xml header
if ($hasXMLHeader) {

View File

@ -3,13 +3,13 @@
namespace SilverStripe\Core;
use Exception;
use ReflectionClass;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Director;
use SilverStripe\Core\Manifest\ClassLoader;
use SilverStripe\Dev\Deprecation;
use SilverStripe\ORM\ArrayLib;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\DataObject;
use ReflectionClass;
use SilverStripe\ORM\DB;
/**
* Provides introspection information about the class tree.
@ -20,28 +20,6 @@ use ReflectionClass;
*/
class ClassInfo
{
/**
* Wrapper for classes getter.
*
* @return array
*/
public static function allClasses()
{
return ClassLoader::inst()->getManifest()->getClasses();
}
/**
* Returns true if a class or interface name exists.
*
* @param string $class
* @return bool
*/
public static function exists($class)
{
return class_exists($class, false) || interface_exists($class, false) || ClassLoader::inst()->getItemPath($class);
}
/**
* Cache for {@link hasTable()}
*
@ -64,6 +42,14 @@ class ClassInfo
*/
private static $_cache_parse = [];
/**
* Cache for has_method_from
*
* @internal
* @var array
*/
private static $_cache_methods = array();
/**
* Cache for class_name
*
@ -72,6 +58,29 @@ class ClassInfo
*/
private static $_cache_class_names = [];
/**
* Wrapper for classes getter.
*
* @return array List of all class names
*/
public static function allClasses()
{
return ClassLoader::inst()->getManifest()->getClassNames();
}
/**
* Returns true if a class or interface name exists.
*
* @param string $class
* @return bool
*/
public static function exists($class)
{
return class_exists($class, false)
|| interface_exists($class, false)
|| ClassLoader::inst()->getItemPath($class);
}
/**
* @todo Move this to SS_Database or DB
*
@ -101,7 +110,7 @@ class ClassInfo
* types that don't exist as implemented classes. By default these are excluded.
* @return array List of subclasses
*/
public static function getValidSubClasses($class = 'SilverStripe\\CMS\\Model\\SiteTree', $includeUnbacked = false)
public static function getValidSubClasses($class = SiteTree::class, $includeUnbacked = false)
{
if (is_string($class) && !class_exists($class)) {
return array();
@ -129,29 +138,26 @@ class ClassInfo
public static function dataClassesFor($nameOrObject)
{
if (is_string($nameOrObject) && !class_exists($nameOrObject)) {
return array();
return [];
}
$result = array();
// Get all classes
$class = self::class_name($nameOrObject);
$classes = array_merge(
self::ancestry($class),
self::subclassesFor($class)
);
foreach ($classes as $class) {
if (DataObject::getSchema()->classHasTable($class)) {
$result[$class] = $class;
}
}
return $result;
// Filter by table
return array_filter($classes, function ($next) {
return DataObject::getSchema()->classHasTable($next);
});
}
/**
* @deprecated 4.0..5.0
* @param string $class
* @return string
*/
public static function baseDataClass($class)
{
@ -163,19 +169,20 @@ class ClassInfo
* Returns a list of classes that inherit from the given class.
* The resulting array includes the base class passed
* through the $class parameter as the first array value.
* Note that keys are lowercase, while the values are correct case.
*
* Example usage:
* <code>
* ClassInfo::subclassesFor('BaseClass');
* array(
* 'BaseClass' => 'BaseClass',
* 'ChildClass' => 'ChildClass',
* 'GrandChildClass' => 'GrandChildClass'
* 'baseclass' => 'BaseClass',
* 'childclass' => 'ChildClass',
* 'grandchildclass' => 'GrandChildClass'
* )
* </code>
*
* @param string|object $nameOrObject The classname or object
* @return array Names of all subclasses as an associative array.
* @return array List of class names with lowercase keys and correct-case values
*/
public static function subclassesFor($nameOrObject)
{
@ -183,16 +190,16 @@ class ClassInfo
return [];
}
//normalise class case
// Get class names
$className = self::class_name($nameOrObject);
$descendants = ClassLoader::inst()->getManifest()->getDescendantsOf($className);
$result = array($className => $className);
$lowerClassName = strtolower($className);
if ($descendants) {
return $result + ArrayLib::valuekey($descendants);
} else {
return $result;
}
// Merge with descendants
$descendants = ClassLoader::inst()->getManifest()->getDescendantsOf($className);
return array_merge(
[ $lowerClassName => $className ],
$descendants
);
}
/**
@ -211,8 +218,15 @@ class ClassInfo
$key = strtolower($nameOrObject);
if (!isset(static::$_cache_class_names[$key])) {
$reflection = new ReflectionClass($nameOrObject);
static::$_cache_class_names[$key] = $reflection->getName();
// Get manifest name
$name = ClassLoader::inst()->getManifest()->getItemName($nameOrObject);
// Use reflection for non-manifest classes
if (!$name) {
$reflection = new ReflectionClass($nameOrObject);
$name = $reflection->getName();
}
static::$_cache_class_names[$key] = $name;
}
return static::$_cache_class_names[$key];
@ -224,25 +238,25 @@ class ClassInfo
*
* @param string|object $nameOrObject Class or object instance
* @param bool $tablesOnly Only return classes that have a table in the db.
* @return array
* @return array List of class names with lowercase keys and correct-case values
*/
public static function ancestry($nameOrObject, $tablesOnly = false)
{
if (is_string($nameOrObject) && !class_exists($nameOrObject)) {
return array();
return [];
}
$class = self::class_name($nameOrObject);
$lClass = strtolower($class);
$lowerClass = strtolower($class);
$cacheKey = $lClass . '_' . (string)$tablesOnly;
$cacheKey = $lowerClass . '_' . (string)$tablesOnly;
$parent = $class;
if (!isset(self::$_cache_ancestry[$cacheKey])) {
$ancestry = array();
$ancestry = [];
do {
if (!$tablesOnly || DataObject::getSchema()->classHasTable($parent)) {
$ancestry[$parent] = $parent;
$ancestry[strtolower($parent)] = $parent;
}
} while ($parent = get_parent_class($parent));
self::$_cache_ancestry[$cacheKey] = array_reverse($ancestry);
@ -253,8 +267,8 @@ class ClassInfo
/**
* @param string $interfaceName
* @return array A self-keyed array of class names. Note that this is only available with Silverstripe
* classes and not built-in PHP classes.
* @return array A self-keyed array of class names with lowercase keys and correct-case values.
* Note that this is only available with Silverstripe classes and not built-in PHP classes.
*/
public static function implementorsOf($interfaceName)
{
@ -270,28 +284,28 @@ class ClassInfo
*/
public static function classImplements($className, $interfaceName)
{
return in_array($className, self::implementorsOf($interfaceName));
$lowerClassName = strtolower($className);
$implementors = self::implementorsOf($interfaceName);
return isset($implementors[$lowerClassName]);
}
/**
* Get all classes contained in a file.
* @uses ManifestBuilder
*
* @todo Doesn't return additional classes that only begin
* with the filename, and have additional naming separated through underscores.
*
* @param string $filePath Path to a PHP file (absolute or relative to webroot)
* @return array
* @return array Map of lowercase class names to correct class name
*/
public static function classes_for_file($filePath)
{
$absFilePath = Director::getAbsFile($filePath);
$matchedClasses = array();
$manifest = ClassLoader::inst()->getManifest()->getClasses();
$absFilePath = Director::getAbsFile($filePath);
$classManifest = ClassLoader::inst()->getManifest();
$classes = $classManifest->getClasses();
$classNames = $classManifest->getClassNames();
foreach ($manifest as $class => $compareFilePath) {
if ($absFilePath == $compareFilePath) {
$matchedClasses[] = $class;
$matchedClasses = [];
foreach ($classes as $lowerClass => $compareFilePath) {
if (strcasecmp($absFilePath, $compareFilePath) === 0) {
$matchedClasses[$lowerClass] = $classNames[$lowerClass];
}
}
@ -301,50 +315,55 @@ class ClassInfo
/**
* Returns all classes contained in a certain folder.
*
* @todo Doesn't return additional classes that only begin
* with the filename, and have additional naming separated through underscores.
*
* @param string $folderPath Relative or absolute folder path
* @return array Array of class names
* @return array Map of lowercase class names to correct class name
*/
public static function classes_for_folder($folderPath)
{
$absFolderPath = Director::getAbsFile($folderPath);
$matchedClasses = array();
$manifest = ClassLoader::inst()->getManifest()->getClasses();
$absFolderPath = Director::getAbsFile($folderPath);
$classManifest = ClassLoader::inst()->getManifest();
$classes = $classManifest->getClasses();
$classNames = $classManifest->getClassNames();
foreach ($manifest as $class => $compareFilePath) {
$matchedClasses = [];
foreach ($classes as $lowerClass => $compareFilePath) {
if (stripos($compareFilePath, $absFolderPath) === 0) {
$matchedClasses[] = $class;
$matchedClasses[$lowerClass] = $classNames[$lowerClass];
}
}
return $matchedClasses;
}
private static $method_from_cache = array();
/**
* Determine if the given class method is implemented at the given comparison class
*
* @param string $class Class to get methods from
* @param string $method Method name to lookup
* @param string $compclass Parent class to test if this is the implementor
* @return bool True if $class::$method is declared in $compclass
*/
public static function has_method_from($class, $method, $compclass)
{
$lClass = strtolower($class);
$lMethod = strtolower($method);
$lCompclass = strtolower($compclass);
if (!isset(self::$method_from_cache[$lClass])) {
self::$method_from_cache[$lClass] = array();
if (!isset(self::$_cache_methods[$lClass])) {
self::$_cache_methods[$lClass] = array();
}
if (!array_key_exists($lMethod, self::$method_from_cache[$lClass])) {
self::$method_from_cache[$lClass][$lMethod] = false;
if (!array_key_exists($lMethod, self::$_cache_methods[$lClass])) {
self::$_cache_methods[$lClass][$lMethod] = false;
$classRef = new ReflectionClass($class);
if ($classRef->hasMethod($method)) {
$methodRef = $classRef->getMethod($method);
self::$method_from_cache[$lClass][$lMethod] = $methodRef->getDeclaringClass()->getName();
self::$_cache_methods[$lClass][$lMethod] = $methodRef->getDeclaringClass()->getName();
}
}
return strtolower(self::$method_from_cache[$lClass][$lMethod]) == $lCompclass;
return strtolower(self::$_cache_methods[$lClass][$lMethod]) === $lCompclass;
}
/**
@ -364,8 +383,9 @@ class ClassInfo
*/
public static function shortName($nameOrObject)
{
$reflection = new ReflectionClass($nameOrObject);
return $reflection->getShortName();
$name = static::class_name($nameOrObject);
$parts = explode('\\', $name);
return end($parts);
}
/**

View File

@ -112,8 +112,9 @@ class CoreConfigFactory
public function buildStaticTransformer()
{
return new PrivateStaticTransformer(function () {
$classes = ClassLoader::inst()->getManifest()->getClasses();
return array_keys($classes);
return ClassLoader::inst()
->getManifest()
->getClassNames();
});
}

View File

@ -31,6 +31,7 @@ class InheritanceMiddleware implements Middleware
$nextConfig = $next($nextClass, $excludeMiddleware);
$config = Priority::mergeArray($nextConfig, $config);
}
return $config;
}
}

View File

@ -19,6 +19,8 @@ use SilverStripe\Dev\TestOnly;
* - Class and interface names and paths.
* - All direct and indirect descendants of a class.
* - All implementors of an interface.
*
* To be consistent; In general all array keys are lowercase, and array values are correct-case
*/
class ClassManifest
{
@ -51,21 +53,51 @@ class ClassManifest
protected $cacheKey;
/**
* Map of classes to paths
* Array of properties to cache
*
* @var array
*/
protected $serialisedProperties = [
'classes',
'classNames',
'descendants',
'interfaces',
'interfaceNames',
'implementors',
'traits',
'traitNames',
];
/**
* Map of lowercase class names to paths
*
* @var array
*/
protected $classes = array();
/**
* Map of lowercase class names to case-correct names
*
* @var array
*/
protected $classNames = [];
/**
* List of root classes with no parent class
* Keys are lowercase, values are correct case.
*
* Note: Only used while regenerating cache
*
* @var array
*/
protected $roots = array();
/**
* List of direct children for any class
* List of direct children for any class.
* Keys are lowercase, values are arrays.
* Each item-value array has lowercase keys and correct case for values.
*
* Note: Only used while regenerating cache
*
* @var array
*/
@ -73,31 +105,49 @@ class ClassManifest
/**
* List of descendents for any class (direct + indirect children)
* Keys are lowercase, values are arrays.
* Each item-value array has lowercase keys and correct case for values.
*
* @var array
*/
protected $descendants = array();
/**
* List of interfaces and paths to those files
* Map of lowercase interface name to path those files
*
* @var array
*/
protected $interfaces = array();
protected $interfaces = [];
/**
* Map of lowercase interface name to proper case
*
* @var array
*/
protected $interfaceNames = [];
/**
* List of direct implementors of any interface
* Keys are lowercase, values are arrays.
* Each item-value array has lowercase keys and correct case for values.
*
* @var array
*/
protected $implementors = array();
/**
* Map of traits to paths
* Map of lowercase trait names to paths
*
* @var array
*/
protected $traits = array();
protected $traits = [];
/**
* Map of lowercase trait names to proper case
*
* @var array
*/
protected $traitNames = [];
/**
* PHP Parser for parsing found files
@ -141,20 +191,22 @@ class ClassManifest
// build cache from factory
if ($this->cacheFactory) {
$this->cache = $this->cacheFactory->create(
CacheInterface::class.'.classmanifest',
[ 'namespace' => 'classmanifest' . ($includeTests ? '_tests' : '') ]
CacheInterface::class . '.classmanifest',
['namespace' => 'classmanifest' . ($includeTests ? '_tests' : '')]
);
}
if (!$forceRegen && $this->cache && ($data = $this->cache->get($this->cacheKey))) {
$this->classes = $data['classes'];
$this->descendants = $data['descendants'];
$this->interfaces = $data['interfaces'];
$this->implementors = $data['implementors'];
$this->traits = $data['traits'];
} else {
$this->regenerate($includeTests);
// Check if cache is safe to use
if (!$forceRegen
&& $this->cache
&& ($data = $this->cache->get($this->cacheKey))
&& $this->loadState($data)
) {
return;
}
// Build
$this->regenerate($includeTests);
}
/**
@ -171,6 +223,11 @@ class ClassManifest
return $this->parser;
}
/**
* Get node traverser for parsing class files
*
* @return NodeTraverser
*/
public function getTraverser()
{
if (!$this->traverser) {
@ -182,6 +239,11 @@ class ClassManifest
return $this->traverser;
}
/**
* Get visitor for parsing class files
*
* @return ClassManifestVisitor
*/
public function getVisitor()
{
if (!$this->visitor) {
@ -200,15 +262,35 @@ class ClassManifest
*/
public function getItemPath($name)
{
$name = strtolower($name);
$lowerName = strtolower($name);
foreach ([
$this->classes,
$this->interfaces,
$this->traits
] as $source) {
if (isset($source[$name]) && file_exists($source[$name])) {
return $source[$name];
$this->classes,
$this->interfaces,
$this->traits,
] as $source) {
if (isset($source[$lowerName]) && file_exists($source[$lowerName])) {
return $source[$lowerName];
}
}
return null;
}
/**
* Return correct case name
*
* @param string $name
* @return string Correct case name
*/
public function getItemName($name)
{
$lowerName = strtolower($name);
foreach ([
$this->classNames,
$this->interfaceNames,
$this->traitNames,
] as $source) {
if (isset($source[$lowerName])) {
return $source[$lowerName];
}
}
return null;
@ -225,23 +307,33 @@ class ClassManifest
}
/**
* Returns a lowercase array of all the class names in the manifest.
* Returns a map of lowercase class names to proper class names in the manifest
*
* @return array
*/
public function getClassNames()
{
return array_keys($this->classes);
return $this->classNames;
}
/**
* Returns a lowercase array of all trait names in the manifest
* Returns a map of lowercased trait names to file paths.
*
* @return array
*/
public function getTraits()
{
return $this->traits;
}
/**
* Returns a map of lowercase trait names to proper trait names in the manifest
*
* @return array
*/
public function getTraitNames()
{
return array_keys($this->traits);
return $this->traitNames;
}
/**
@ -268,12 +360,11 @@ class ClassManifest
}
$lClass = strtolower($class);
if (array_key_exists($lClass, $this->descendants)) {
return $this->descendants[$lClass];
} else {
return array();
}
return [];
}
/**
@ -286,6 +377,16 @@ class ClassManifest
return $this->interfaces;
}
/**
* Return map of lowercase interface names to proper case names in the manifest
*
* @return array
*/
public function getInterfaceNames()
{
return $this->interfaceNames;
}
/**
* Returns a map of lowercased interface names to the classes the implement
* them.
@ -301,15 +402,14 @@ class ClassManifest
* Returns an array containing the class names that implement a certain
* interface.
*
* @param string $interface
* @param string $interface
* @return array
*/
public function getImplementorsOf($interface)
{
$interface = strtolower($interface);
if (array_key_exists($interface, $this->implementors)) {
return $this->implementors[$interface];
$lowerInterface = strtolower($interface);
if (array_key_exists($lowerInterface, $this->implementors)) {
return $this->implementors[$lowerInterface];
} else {
return array();
}
@ -334,21 +434,16 @@ class ClassManifest
*/
public function regenerate($includeTests)
{
$resets = array(
'classes', 'roots', 'children', 'descendants', 'interfaces',
'implementors', 'traits'
);
// Reset the manifest so stale info doesn't cause errors.
foreach ($resets as $reset) {
$this->$reset = array();
}
$this->loadState([]);
$this->roots = [];
$this->children = [];
$finder = new ManifestFileFinder();
$finder->setOptions(array(
'name_regex' => '/^[^_].*\\.php$/',
'ignore_files' => array('index.php', 'main.php', 'cli-script.php'),
'ignore_tests' => !$includeTests,
'name_regex' => '/^[^_].*\\.php$/',
'ignore_files' => array('index.php', 'main.php', 'cli-script.php'),
'ignore_tests' => !$includeTests,
'file_callback' => function ($basename, $pathname) use ($includeTests) {
$this->handleFile($basename, $pathname, $includeTests);
},
@ -360,23 +455,21 @@ class ClassManifest
}
if ($this->cache) {
$data = array(
'classes' => $this->classes,
'descendants' => $this->descendants,
'interfaces' => $this->interfaces,
'implementors' => $this->implementors,
'traits' => $this->traits,
);
$data = $this->getState();
$this->cache->set($this->cacheKey, $data);
}
}
/**
* Visit a file to inspect for classes, interfaces and traits
*
* @param string $basename
* @param string $pathname
* @param bool $includeTests
* @throws Exception
*/
public function handleFile($basename, $pathname, $includeTests)
{
$classes = null;
$interfaces = null;
$traits = null;
// The results of individual file parses are cached, since only a few
// files will have changed and TokenisedRegularExpression is quite
// slow. A combination of the file name and file contents hash are used,
@ -384,6 +477,7 @@ class ClassManifest
$key = preg_replace('/[^a-zA-Z0-9_]/', '_', $basename) . '_' . md5_file($pathname);
// Attempt to load from cache
// Note: $classes, $interfaces and $traits arrays have correct-case keys, not lowercase
$changed = false;
if ($this->cache
&& ($data = $this->cache->get($key))
@ -409,64 +503,64 @@ class ClassManifest
$traits = $this->getVisitor()->getTraits();
}
// Merge this data into the global list
// Merge raw class data into global list
foreach ($classes as $className => $classInfo) {
$extends = !empty($classInfo['extends'])
? array_map('strtolower', $classInfo['extends'])
: [];
$implements = !empty($classInfo['interfaces'])
? array_map('strtolower', $classInfo['interfaces'])
: [];
$lowercaseName = strtolower($className);
if (array_key_exists($lowercaseName, $this->classes)) {
$lowerClassName = strtolower($className);
if (array_key_exists($lowerClassName, $this->classes)) {
throw new Exception(sprintf(
'There are two files containing the "%s" class: "%s" and "%s"',
$className,
$this->classes[$lowercaseName],
$this->classes[$lowerClassName],
$pathname
));
}
// Skip if implements TestOnly, but doesn't include tests
if (!$includeTests
&& $implements
&& in_array(strtolower(TestOnly::class), $implements)
) {
$lowerInterfaces = array_map('strtolower', $classInfo['interfaces']);
if (!$includeTests && in_array(strtolower(TestOnly::class), $lowerInterfaces)) {
$changed = true;
unset($classes[$className]);
continue;
}
$this->classes[$lowercaseName] = $pathname;
$this->classes[$lowerClassName] = $pathname;
$this->classNames[$lowerClassName] = $className;
if ($extends) {
foreach ($extends as $ancestor) {
if (!isset($this->children[$ancestor])) {
$this->children[$ancestor] = array($className);
} else {
$this->children[$ancestor][] = $className;
// Add to children
if ($classInfo['extends']) {
foreach ($classInfo['extends'] as $ancestor) {
$lowerAncestor = strtolower($ancestor);
if (!isset($this->children[$lowerAncestor])) {
$this->children[$lowerAncestor] = [];
}
$this->children[$lowerAncestor][$lowerClassName] = $className;
}
} else {
$this->roots[] = $className;
$this->roots[$lowerClassName] = $className;
}
if ($implements) {
foreach ($implements as $interface) {
if (!isset($this->implementors[$interface])) {
$this->implementors[$interface] = array($className);
} else {
$this->implementors[$interface][] = $className;
}
// Load interfaces
foreach ($classInfo['interfaces'] as $interface) {
$lowerInterface = strtolower($interface);
if (!isset($this->implementors[$lowerInterface])) {
$this->implementors[$lowerInterface] = [];
}
$this->implementors[$lowerInterface][$lowerClassName] = $className;
}
}
// Merge all found interfaces into list
foreach ($interfaces as $interfaceName => $interfaceInfo) {
$this->interfaces[strtolower($interfaceName)] = $pathname;
$lowerInterface = strtolower($interfaceName);
$this->interfaces[$lowerInterface] = $pathname;
$this->interfaceNames[$lowerInterface] = $interfaceName;
}
// Merge all traits
foreach ($traits as $traitName => $traitInfo) {
$this->traits[strtolower($traitName)] = $pathname;
$lowerTrait = strtolower($traitName);
$this->traits[$lowerTrait] = $pathname;
$this->traitNames[$lowerTrait] = $traitName;
}
// Save back to cache if configured
@ -489,23 +583,57 @@ class ClassManifest
*/
protected function coalesceDescendants($class)
{
$lClass = strtolower($class);
if (array_key_exists($lClass, $this->children)) {
$this->descendants[$lClass] = array();
foreach ($this->children[$lClass] as $class) {
$this->descendants[$lClass] = array_merge(
$this->descendants[$lClass],
array($class),
$this->coalesceDescendants($class)
);
}
return $this->descendants[$lClass];
} else {
return array();
// Reset descendents to immediate children initially
$lowerClass = strtolower($class);
if (empty($this->children[$lowerClass])) {
return [];
}
// Coalasce children into descendent list
$this->descendants[$lowerClass] = $this->children[$lowerClass];
foreach ($this->children[$lowerClass] as $childClass) {
// Merge all nested descendants
$this->descendants[$lowerClass] = array_merge(
$this->descendants[$lowerClass],
$this->coalesceDescendants($childClass)
);
}
return $this->descendants[$lowerClass];
}
/**
* Reload state from given cache data
*
* @param array $data
* @return bool True if cache was valid and successfully loaded
*/
protected function loadState($data)
{
$success = true;
foreach ($this->serialisedProperties as $property) {
if (!isset($data[$property]) || !is_array($data[$property])) {
$success = false;
$value = [];
} else {
$value = $data[$property];
}
$this->$property = $value;
}
return $success;
}
/**
* Load current state into an array of data
*
* @return array
*/
protected function getState()
{
$data = [];
foreach ($this->serialisedProperties as $property) {
$data[$property] = $this->$property;
}
return $data;
}
/**
@ -516,6 +644,9 @@ class ClassManifest
*/
protected function validateItemCache($data)
{
if (!$data || !is_array($data)) {
return false;
}
foreach (['classes', 'interfaces', 'traits'] as $key) {
// Must be set
if (!isset($data[$key])) {

View File

@ -5,6 +5,7 @@ namespace SilverStripe\Core\Manifest;
use LogicException;
use Psr\SimpleCache\CacheInterface;
use SilverStripe\Core\Cache\CacheFactory;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Injector\Injector;
@ -50,6 +51,22 @@ class ModuleManifest
*/
protected $modules = [];
/**
* List of modules sorted by priority
*
* @config
* @var array
*/
private static $module_priority = [];
/**
* Project name
*
* @config
* @var string
*/
private static $project = null;
/**
* Adds a path as a module
*
@ -244,6 +261,7 @@ class ModuleManifest
{
$order = static::config()->uninherited('module_priority');
$project = static::config()->get('project');
/* @var PrioritySorter $sorter */
$sorter = Injector::inst()->createWithArgs(
PrioritySorter::class . '.modulesorter',

View File

@ -116,7 +116,7 @@ class TaskRunner extends Controller
{
$availableTasks = array();
$taskClasses = ClassInfo::subclassesFor('SilverStripe\\Dev\\BuildTask');
$taskClasses = ClassInfo::subclassesFor(BuildTask::class);
// remove the base class
array_shift($taskClasses);

View File

@ -167,7 +167,7 @@ class DatabaseAdmin extends Controller
*/
public function buildDefaults()
{
$dataClasses = ClassInfo::subclassesFor('SilverStripe\ORM\DataObject');
$dataClasses = ClassInfo::subclassesFor(DataObject::class);
array_shift($dataClasses);
if (!Director::is_cli()) {
@ -260,7 +260,7 @@ class DatabaseAdmin extends Controller
}
// Build the database. Most of the hard work is handled by DataObject
$dataClasses = ClassInfo::subclassesFor('SilverStripe\ORM\DataObject');
$dataClasses = ClassInfo::subclassesFor(DataObject::class);
array_shift($dataClasses);
if (!$quiet) {

View File

@ -129,8 +129,9 @@ class DBClassName extends DBEnum
public function getEnum()
{
$classNames = ClassInfo::subclassesFor($this->getBaseClass());
unset($classNames[DataObject::class]);
return $classNames;
$dataobject = strtolower(DataObject::class);
unset($classNames[$dataobject]);
return array_values($classNames);
}
/**

View File

@ -37,9 +37,8 @@ class PolymorphicHasManyList extends HasManyList
* to generate the ID and Class foreign keys.
* @param string $foreignClass Name of the class filter this relation is filtered against
*/
function __construct($dataClass, $foreignField, $foreignClass)
public function __construct($dataClass, $foreignField, $foreignClass)
{
// Set both id foreign key (as in HasManyList) and the class foreign key
parent::__construct($dataClass, "{$foreignField}ID");
$this->classForeignKey = "{$foreignField}Class";
@ -120,7 +119,8 @@ class PolymorphicHasManyList extends HasManyList
$foreignClass = $this->getForeignClass();
$classNames = ClassInfo::subclassesFor($foreignClass);
$classForeignKey = $this->classForeignKey;
if (!in_array($item->$classForeignKey, $classNames)) {
$classValueLower = strtolower($item->$classForeignKey);
if (!array_key_exists($classValueLower, $classNames)) {
return;
}

View File

@ -3,6 +3,7 @@
namespace SilverStripe\Core\Tests;
use ReflectionException;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Tests\ClassInfoTest\BaseClass;
use SilverStripe\Core\Tests\ClassInfoTest\BaseDataClass;
use SilverStripe\Core\Tests\ClassInfoTest\ChildClass;
@ -11,8 +12,6 @@ use SilverStripe\Core\Tests\ClassInfoTest\HasFields;
use SilverStripe\Core\Tests\ClassInfoTest\NoFields;
use SilverStripe\Core\Tests\ClassInfoTest\WithCustomTable;
use SilverStripe\Core\Tests\ClassInfoTest\WithRelation;
use SilverStripe\ORM\ArrayLib;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\DataObject;
use SilverStripe\View\ViewableData;
@ -50,22 +49,19 @@ class ClassInfoTest extends SapphireTest
public function testSubclassesFor()
{
$subclasses = [
'silverstripe\\core\\tests\\classinfotest\\baseclass' => BaseClass::class,
'silverstripe\\core\\tests\\classinfotest\\childclass' => ChildClass::class,
'silverstripe\\core\\tests\\classinfotest\\grandchildclass' => GrandChildClass::class,
];
$this->assertEquals(
array(
BaseClass::class => BaseClass::class,
ChildClass::class => ChildClass::class,
GrandChildClass::class => GrandChildClass::class
),
$subclasses,
ClassInfo::subclassesFor(BaseClass::class),
'ClassInfo::subclassesFor() returns only direct subclasses and doesnt include base class'
);
ClassInfo::reset_db_cache();
$this->assertEquals(
array(
BaseClass::class => BaseClass::class,
ChildClass::class => ChildClass::class,
GrandChildClass::class => GrandChildClass::class
),
$subclasses,
ClassInfo::subclassesFor('silverstripe\\core\\tests\\classinfotest\\baseclass'),
'ClassInfo::subclassesFor() is acting in a case sensitive way when it should not'
);
@ -96,20 +92,27 @@ class ClassInfoTest extends SapphireTest
public function testClassesForFolder()
{
//$baseFolder = Director::baseFolder() . '/' . FRAMEWORK_DIR . '/tests/_ClassInfoTest';
//$manifestInfo = ManifestBuilder::get_manifest_info($baseFolder);
$classes = ClassInfo::classes_for_folder(ltrim(FRAMEWORK_DIR . '/tests', '/'));
$this->assertContains(
$this->assertArrayHasKey(
'silverstripe\\core\\tests\\classinfotest',
$classes,
'ClassInfo::classes_for_folder() returns classes matching the filename'
);
$this->assertContains(
ClassInfoTest::class,
$classes,
'ClassInfo::classes_for_folder() returns classes matching the filename'
);
$this->assertArrayHasKey(
'silverstripe\\core\\tests\\classinfotest\\baseclass',
$classes,
'ClassInfo::classes_for_folder() returns additional classes not matching the filename'
);
$this->assertContains(
BaseClass::class,
$classes,
'ClassInfo::classes_for_folder() returns additional classes not matching the filename'
);
}
/**
@ -118,12 +121,12 @@ class ClassInfoTest extends SapphireTest
public function testAncestry()
{
$ancestry = ClassInfo::ancestry(ChildClass::class);
$expect = ArrayLib::valuekey([
ViewableData::class,
DataObject::class,
BaseClass::class,
ChildClass::class,
]);
$expect = [
'silverstripe\\view\\viewabledata' => ViewableData::class,
'silverstripe\\orm\\dataobject' => DataObject::class,
'silverstripe\\core\tests\classinfotest\\baseclass' => BaseClass::class,
'silverstripe\\core\tests\classinfotest\\childclass' => ChildClass::class,
];
$this->assertEquals($expect, $ancestry);
ClassInfo::reset_db_cache();
@ -135,7 +138,9 @@ class ClassInfoTest extends SapphireTest
ClassInfo::reset_db_cache();
$ancestry = ClassInfo::ancestry(ChildClass::class, true);
$this->assertEquals(
array(BaseClass::class => BaseClass::class),
[
'silverstripe\\core\tests\classinfotest\\baseclass' => BaseClass::class
],
$ancestry,
'$tablesOnly option excludes memory-only inheritance classes'
);
@ -146,13 +151,12 @@ class ClassInfoTest extends SapphireTest
*/
public function testDataClassesFor()
{
$expect = array(
BaseDataClass::class => BaseDataClass::class,
HasFields::class => HasFields::class,
WithRelation::class => WithRelation::class,
WithCustomTable::class => WithCustomTable::class,
);
$expect = [
'silverstripe\\core\\tests\\classinfotest\\basedataclass' => BaseDataClass::class,
'silverstripe\\core\\tests\\classinfotest\\hasfields' => HasFields::class,
'silverstripe\\core\\tests\\classinfotest\\withrelation' => WithRelation::class,
'silverstripe\\core\\tests\\classinfotest\\withcustomtable' => WithCustomTable::class,
];
$classes = array(
BaseDataClass::class,
NoFields::class,
@ -166,10 +170,10 @@ class ClassInfoTest extends SapphireTest
ClassInfo::reset_db_cache();
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[1]));
$expect = array(
BaseDataClass::class => BaseDataClass::class,
HasFields::class => HasFields::class,
);
$expect = [
'silverstripe\\core\\tests\\classinfotest\\basedataclass' => BaseDataClass::class,
'silverstripe\\core\\tests\\classinfotest\\hasfields' => HasFields::class,
];
ClassInfo::reset_db_cache();
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[2]));

View File

@ -71,7 +71,13 @@ class ClassManifestTest extends SapphireTest
public function testGetClassNames()
{
$this->assertEquals(
['classa', 'classb', 'classc', 'classd', 'classe'],
[
'classa' => 'ClassA',
'classb' => 'ClassB',
'classc' => 'ClassC',
'classd' => 'ClassD',
'classe' => 'ClassE',
],
$this->manifest->getClassNames()
);
}
@ -79,28 +85,36 @@ class ClassManifestTest extends SapphireTest
public function testGetTraitNames()
{
$this->assertEquals(
array('testtraita', 'testnamespace\testing\testtraitb'),
array(
'testtraita' => 'TestTraitA',
'testnamespace\testing\testtraitb' => 'TestNamespace\Testing\TestTraitB',
),
$this->manifest->getTraitNames()
);
}
public function testGetDescendants()
{
$expect = array(
'classa' => array('ClassC', 'ClassD'),
'classc' => array('ClassD')
);
$expect = [
'classa' => [
'classc' => 'ClassC',
'classd' => 'ClassD',
],
'classc' => [
'classd' => 'ClassD',
],
];
$this->assertEquals($expect, $this->manifest->getDescendants());
}
public function testGetDescendantsOf()
{
$expect = array(
'CLASSA' => array('ClassC', 'ClassD'),
'classa' => array('ClassC', 'ClassD'),
'CLASSC' => array('ClassD'),
'classc' => array('ClassD')
);
$expect = [
'CLASSA' => ['classc' => 'ClassC', 'classd' => 'ClassD'],
'classa' => ['classc' => 'ClassC', 'classd' => 'ClassD'],
'CLASSC' => ['classd' => 'ClassD'],
'classc' => ['classd' => 'ClassD'],
];
foreach ($expect as $class => $desc) {
$this->assertEquals($desc, $this->manifest->getDescendantsOf($class));
@ -118,21 +132,21 @@ class ClassManifestTest extends SapphireTest
public function testGetImplementors()
{
$expect = array(
'interfacea' => array('ClassB'),
'interfaceb' => array('ClassC')
);
$expect = [
'interfacea' => ['classb' => 'ClassB'],
'interfaceb' => ['classc' => 'ClassC'],
];
$this->assertEquals($expect, $this->manifest->getImplementors());
}
public function testGetImplementorsOf()
{
$expect = array(
'INTERFACEA' => array('ClassB'),
'interfacea' => array('ClassB'),
'INTERFACEB' => array('ClassC'),
'interfaceb' => array('ClassC')
);
$expect = [
'INTERFACEA' => ['classb' => 'ClassB'],
'interfacea' => ['classb' => 'ClassB'],
'INTERFACEB' => ['classc' => 'ClassC'],
'interfaceb' => ['classc' => 'ClassC'],
];
foreach ($expect as $interface => $impl) {
$this->assertEquals($impl, $this->manifest->getImplementorsOf($interface));
@ -141,13 +155,13 @@ class ClassManifestTest extends SapphireTest
public function testTestManifestIncludesTestClasses()
{
$this->assertNotContains('testclassa', array_keys($this->manifest->getClasses()));
$this->assertContains('testclassa', array_keys($this->manifestTests->getClasses()));
$this->assertArrayNotHasKey('testclassa', $this->manifest->getClasses());
$this->assertArrayHasKey('testclassa', $this->manifestTests->getClasses());
}
public function testManifestExcludeFilesPrefixedWithUnderscore()
{
$this->assertNotContains('ignore', array_keys($this->manifest->getClasses()));
$this->assertArrayNotHasKey('ignore', $this->manifest->getClasses());
}
/**

View File

@ -2,11 +2,13 @@
namespace SilverStripe\Core\Tests\Manifest;
use SilverStripe\Admin\ModelAdmin;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Manifest\ClassManifest;
use SilverStripe\Core\Manifest\ClassLoader;
use SilverStripe\Dev\SapphireTest;
use ReflectionMethod;
use SilverStripe\Security\PermissionProvider;
/**
* Tests for the {@link ClassManifest} class.
@ -41,27 +43,29 @@ class NamespacedClassManifestTest extends SapphireTest
public function testClassInfoIsCorrect()
{
$this->assertContains('SilverStripe\Framework\Tests\ClassI', ClassInfo::implementorsOf('SilverStripe\\Security\\PermissionProvider'));
$this->assertContains(
'SilverStripe\\Framework\\Tests\\ClassI',
ClassInfo::implementorsOf(PermissionProvider::class)
);
// because we're using a nested manifest we have to "coalesce" the descendants again to correctly populate the
// descendants of the core classes we want to test against - this is a limitation of the test manifest not
// including all core classes
$method = new ReflectionMethod($this->manifest, 'coalesceDescendants');
$method->setAccessible(true);
$method->invoke($this->manifest, 'SilverStripe\\Admin\\ModelAdmin');
$this->assertContains('SilverStripe\Framework\Tests\ClassI', ClassInfo::subclassesFor('SilverStripe\\Admin\\ModelAdmin'));
$method->invoke($this->manifest, ModelAdmin::class);
$this->assertContains('SilverStripe\\Framework\\Tests\\ClassI', ClassInfo::subclassesFor(ModelAdmin::class));
}
public function testGetItemPath()
{
$expect = array(
'SILVERSTRIPE\TEST\CLASSA' => 'module/classes/ClassA.php',
'Silverstripe\Test\ClassA' => 'module/classes/ClassA.php',
'silverstripe\test\classa' => 'module/classes/ClassA.php',
'SILVERSTRIPE\TEST\INTERFACEA' => 'module/interfaces/InterfaceA.php',
'Silverstripe\Test\InterfaceA' => 'module/interfaces/InterfaceA.php',
'silverstripe\test\interfacea' => 'module/interfaces/InterfaceA.php'
'SILVERSTRIPE\\TEST\\CLASSA' => 'module/classes/ClassA.php',
'Silverstripe\\Test\\ClassA' => 'module/classes/ClassA.php',
'silverstripe\\test\\classa' => 'module/classes/ClassA.php',
'SILVERSTRIPE\\TEST\\INTERFACEA' => 'module/interfaces/InterfaceA.php',
'Silverstripe\\Test\\InterfaceA' => 'module/interfaces/InterfaceA.php',
'silverstripe\\test\\interfacea' => 'module/interfaces/InterfaceA.php'
);
foreach ($expect as $name => $path) {
@ -72,15 +76,15 @@ class NamespacedClassManifestTest extends SapphireTest
public function testGetClasses()
{
$expect = array(
'silverstripe\test\classa' => "{$this->base}/module/classes/ClassA.php",
'silverstripe\test\classb' => "{$this->base}/module/classes/ClassB.php",
'silverstripe\test\classc' => "{$this->base}/module/classes/ClassC.php",
'silverstripe\test\classd' => "{$this->base}/module/classes/ClassD.php",
'silverstripe\test\classe' => "{$this->base}/module/classes/ClassE.php",
'silverstripe\test\classf' => "{$this->base}/module/classes/ClassF.php",
'silverstripe\test\classg' => "{$this->base}/module/classes/ClassG.php",
'silverstripe\test\classh' => "{$this->base}/module/classes/ClassH.php",
'silverstripe\framework\tests\classi' => "{$this->base}/module/classes/ClassI.php",
'silverstripe\\test\\classa' => "{$this->base}/module/classes/ClassA.php",
'silverstripe\\test\\classb' => "{$this->base}/module/classes/ClassB.php",
'silverstripe\\test\\classc' => "{$this->base}/module/classes/ClassC.php",
'silverstripe\\test\\classd' => "{$this->base}/module/classes/ClassD.php",
'silverstripe\\test\\classe' => "{$this->base}/module/classes/ClassE.php",
'silverstripe\\test\\classf' => "{$this->base}/module/classes/ClassF.php",
'silverstripe\\test\\classg' => "{$this->base}/module/classes/ClassG.php",
'silverstripe\\test\\classh' => "{$this->base}/module/classes/ClassH.php",
'silverstripe\\framework\\tests\\classi' => "{$this->base}/module/classes/ClassI.php",
);
$this->assertEquals($expect, $this->manifest->getClasses());
@ -89,29 +93,45 @@ class NamespacedClassManifestTest extends SapphireTest
public function testGetClassNames()
{
$this->assertEquals(
array('silverstripe\test\classa',
'silverstripe\test\classb', 'silverstripe\test\classc', 'silverstripe\test\classd',
'silverstripe\test\classe', 'silverstripe\test\classf', 'silverstripe\test\classg',
'silverstripe\test\classh', 'silverstripe\framework\tests\classi'),
[
'silverstripe\\test\\classa' => 'silverstripe\\test\\ClassA',
'silverstripe\\test\\classb' => 'silverstripe\\test\\ClassB',
'silverstripe\\test\\classc' => 'silverstripe\\test\\ClassC',
'silverstripe\\test\\classd' => 'silverstripe\\test\\ClassD',
'silverstripe\\test\\classe' => 'silverstripe\\test\\ClassE',
'silverstripe\\test\\classf' => 'silverstripe\\test\\ClassF',
'silverstripe\\test\\classg' => 'silverstripe\\test\\ClassG',
'silverstripe\\test\\classh' => 'silverstripe\\test\\ClassH',
'silverstripe\\framework\\tests\\classi' => 'SilverStripe\\Framework\\Tests\\ClassI',
],
$this->manifest->getClassNames()
);
}
public function testGetDescendants()
{
$expect = array(
'silverstripe\test\classa' => array('silverstripe\test\ClassB', 'silverstripe\test\ClassH'),
);
$expect = [
'silverstripe\\test\\classa' => [
'silverstripe\\test\\classb' => 'silverstripe\test\ClassB',
'silverstripe\\test\\classh' => 'silverstripe\test\ClassH',
],
];
$this->assertEquals($expect, $this->manifest->getDescendants());
}
public function testGetDescendantsOf()
{
$expect = array(
'SILVERSTRIPE\TEST\CLASSA' => array('silverstripe\test\ClassB', 'silverstripe\test\ClassH'),
'silverstripe\test\classa' => array('silverstripe\test\ClassB', 'silverstripe\test\ClassH'),
);
$expect = [
'SILVERSTRIPE\\TEST\\CLASSA' => [
'silverstripe\\test\\classb' => 'silverstripe\test\ClassB',
'silverstripe\\test\\classh' => 'silverstripe\test\ClassH',
],
'silverstripe\\test\\classa' => [
'silverstripe\\test\\classb' => 'silverstripe\test\ClassB',
'silverstripe\\test\\classh' => 'silverstripe\test\ClassH',
],
];
foreach ($expect as $class => $desc) {
$this->assertEquals($desc, $this->manifest->getDescendantsOf($class));
@ -121,32 +141,52 @@ class NamespacedClassManifestTest extends SapphireTest
public function testGetInterfaces()
{
$expect = array(
'silverstripe\test\interfacea' => "{$this->base}/module/interfaces/InterfaceA.php",
'silverstripe\\test\\interfacea' => "{$this->base}/module/interfaces/InterfaceA.php",
);
$this->assertEquals($expect, $this->manifest->getInterfaces());
}
public function testGetImplementors()
{
$expect = array(
'silverstripe\test\interfacea' => array('silverstripe\test\ClassE'),
'interfacea' => array('silverstripe\test\ClassF'),
'silverstripe\test\subtest\interfacea' => array('silverstripe\test\ClassG'),
'silverstripe\security\permissionprovider' => array('SilverStripe\Framework\Tests\ClassI'),
);
$expect = [
'silverstripe\\test\\interfacea' => [
'silverstripe\\test\\classe' => 'silverstripe\\test\\ClassE',
],
'interfacea' => [
'silverstripe\\test\\classf' => 'silverstripe\\test\\ClassF',
],
'silverstripe\\test\\subtest\\interfacea' => [
'silverstripe\\test\\classg' => 'silverstripe\\test\\ClassG',
],
'silverstripe\\security\\permissionprovider' => [
'silverstripe\\framework\\tests\\classi' => 'SilverStripe\\Framework\\Tests\\ClassI',
],
];
$this->assertEquals($expect, $this->manifest->getImplementors());
}
public function testGetImplementorsOf()
{
$expect = array(
'SILVERSTRIPE\TEST\INTERFACEA' => array('silverstripe\test\ClassE'),
'silverstripe\test\interfacea' => array('silverstripe\test\ClassE'),
'INTERFACEA' => array('silverstripe\test\ClassF'),
'interfacea' => array('silverstripe\test\ClassF'),
'SILVERSTRIPE\TEST\SUBTEST\INTERFACEA' => array('silverstripe\test\ClassG'),
'silverstripe\test\subtest\interfacea' => array('silverstripe\test\ClassG'),
);
$expect = [
'SILVERSTRIPE\\TEST\\INTERFACEA' => [
'silverstripe\\test\\classe' => 'silverstripe\\test\\ClassE',
],
'silverstripe\\test\\interfacea' => [
'silverstripe\\test\\classe' => 'silverstripe\\test\\ClassE',
],
'INTERFACEA' => [
'silverstripe\\test\\classf' => 'silverstripe\\test\\ClassF',
],
'interfacea' => [
'silverstripe\\test\\classf' => 'silverstripe\\test\\ClassF',
],
'SILVERSTRIPE\\TEST\\SUBTEST\\INTERFACEA' => [
'silverstripe\\test\\classg' => 'silverstripe\\test\\ClassG',
],
'silverstripe\\test\\subtest\\interfacea' => [
'silverstripe\\test\\classg' => 'silverstripe\\test\\ClassG',
],
];
foreach ($expect as $interface => $impl) {
$this->assertEquals($impl, $this->manifest->getImplementorsOf($interface));

View File

@ -28,21 +28,27 @@ class DBClassNameTest extends SapphireTest
{
// Object 1 fields
$object = new DBClassNameTest\TestObject();
/** @var DBClassName $defaultClass */
$defaultClass = $object->dbObject('DefaultClass');
/** @var DBClassName $anyClass */
$anyClass = $object->dbObject('AnyClass');
/** @var DBClassName $childClass */
$childClass = $object->dbObject('ChildClass');
/** @var DBClassName $leafClass */
$leafClass = $object->dbObject('LeafClass');
// Object 2 fields
$object2 = new DBClassNameTest\ObjectSubClass();
/** @var DBClassName $midDefault */
$midDefault = $object2->dbObject('MidClassDefault');
/** @var DBClassName $midClass */
$midClass = $object2->dbObject('MidClass');
// Default fields always default to children of base class (even if put in a subclass)
$mainSubclasses = array (
DBClassNameTest\TestObject::class => DBClassNameTest\TestObject::class,
DBClassNameTest\ObjectSubClass::class => DBClassNameTest\ObjectSubClass::class,
DBClassNameTest\ObjectSubSubClass::class => DBClassNameTest\ObjectSubSubClass::class,
DBClassNameTest\TestObject::class,
DBClassNameTest\ObjectSubClass::class,
DBClassNameTest\ObjectSubSubClass::class,
);
$this->assertEquals($mainSubclasses, $defaultClass->getEnumObsolete());
$this->assertEquals($mainSubclasses, $midDefault->getEnumObsolete());
@ -56,15 +62,15 @@ class DBClassNameTest extends SapphireTest
// Classes bound to the middle of a tree
$midSubClasses = $mainSubclasses = array (
DBClassNameTest\ObjectSubClass::class => DBClassNameTest\ObjectSubClass::class,
DBClassNameTest\ObjectSubSubClass::class => DBClassNameTest\ObjectSubSubClass::class,
DBClassNameTest\ObjectSubClass::class,
DBClassNameTest\ObjectSubSubClass::class,
);
$this->assertEquals($midSubClasses, $childClass->getEnumObsolete());
$this->assertEquals($midSubClasses, $midClass->getEnumObsolete());
// Leaf clasess contain only exactly one node
$this->assertEquals(
array(DBClassNameTest\ObjectSubSubClass::class => DBClassNameTest\ObjectSubSubClass::class,),
[ DBClassNameTest\ObjectSubSubClass::class ],
$leafClass->getEnumObsolete()
);
}

View File

@ -2,13 +2,9 @@
namespace SilverStripe\ORM\Tests;
use SilverStripe\Assets\Tests\FileMigrationHelperTest\Extension;
use SilverStripe\Core\Config\Middleware\ExtensionMiddleware;
use SilverStripe\Dev\Debug;
use SilverStripe\ORM\DataObject;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\DataObject;
use SilverStripe\Security\Member;
class DataExtensionTest extends SapphireTest
@ -207,9 +203,7 @@ class DataExtensionTest extends SapphireTest
public function testExtensionAllMethodNamesHasOwner()
{
/**
* @var DataExtensionTest\MyObject $do
*/
/** @var DataExtensionTest\MyObject $do */
$do = DataExtensionTest\MyObject::create();
$this->assertTrue($do->hasMethod('getTestValueWith_MyObject'));

View File

@ -12,7 +12,6 @@ use SilverStripe\ORM\Tests\DataObjectSchemaGenerationTest\TestObject;
class DataObjectSchemaGenerationTest extends SapphireTest
{
protected static $extra_dataobjects = array(
TestObject::class,
TestIndexObject::class
@ -20,6 +19,8 @@ class DataObjectSchemaGenerationTest extends SapphireTest
public static function setUpBeforeClass()
{
// Start tests
static::start();
// enable fulltext option on this table
TestIndexObject::config()->update(
@ -197,6 +198,7 @@ class DataObjectSchemaGenerationTest extends SapphireTest
/**
* Tests the generation of the ClassName spec and ensure it's not unnecessarily influenced
* by the order of classnames of existing records
* @skipUpgrade
*/
public function testClassNameSpecGeneration()
{
@ -206,15 +208,12 @@ class DataObjectSchemaGenerationTest extends SapphireTest
DBClassName::clear_classname_cache();
$do1 = new TestObject();
$fields = $schema->databaseFields(TestObject::class, false);
/**
* @skipUpgrade
*/
$this->assertEquals("DBClassName", $fields['ClassName']);
$this->assertEquals(
array(
TestObject::class => TestObject::class,
TestIndexObject::class => TestIndexObject::class
),
[
TestObject::class,
TestIndexObject::class,
],
$do1->dbObject('ClassName')->getEnum()
);
@ -224,10 +223,10 @@ class DataObjectSchemaGenerationTest extends SapphireTest
$item1->write();
DBClassName::clear_classname_cache();
$this->assertEquals(
array(
TestObject::class => TestObject::class,
TestIndexObject::class => TestIndexObject::class
),
[
TestObject::class,
TestIndexObject::class,
],
$item1->dbObject('ClassName')->getEnum()
);
$item1->delete();
@ -237,10 +236,10 @@ class DataObjectSchemaGenerationTest extends SapphireTest
$item2->write();
DBClassName::clear_classname_cache();
$this->assertEquals(
array(
TestObject::class => TestObject::class,
TestIndexObject::class => TestIndexObject::class
),
[
TestObject::class,
TestIndexObject::class,
],
$item2->dbObject('ClassName')->getEnum()
);
$item2->delete();
@ -252,10 +251,10 @@ class DataObjectSchemaGenerationTest extends SapphireTest
$item2->write();
DBClassName::clear_classname_cache();
$this->assertEquals(
array(
TestObject::class => TestObject::class,
TestIndexObject::class => TestIndexObject::class
),
[
TestObject::class,
TestIndexObject::class,
],
$item1->dbObject('ClassName')->getEnum()
);
$item1->delete();