From 261302a12150b88068a70f829f7265f31e57f551 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Tue, 19 Sep 2017 16:55:39 +1200 Subject: [PATCH] ENHANCEMENT Don't force all class names to lowercase Speeds up autoloading because composer psr-4 works properly now --- src/Control/ContentNegotiator.php | 14 +- src/Core/ClassInfo.php | 200 +++++----- src/Core/Config/CoreConfigFactory.php | 5 +- .../Middleware/InheritanceMiddleware.php | 1 + src/Core/Manifest/ClassManifest.php | 341 ++++++++++++------ src/Core/Manifest/ModuleManifest.php | 18 + src/Dev/TaskRunner.php | 2 +- src/ORM/DatabaseAdmin.php | 4 +- src/ORM/FieldType/DBClassName.php | 5 +- src/ORM/PolymorphicHasManyList.php | 6 +- tests/php/Core/ClassInfoTest.php | 72 ++-- tests/php/Core/Manifest/ClassManifestTest.php | 64 ++-- .../Manifest/NamespacedClassManifestTest.php | 130 ++++--- tests/php/ORM/DBClassNameTest.php | 18 +- tests/php/ORM/DataExtensionTest.php | 10 +- .../ORM/DataObjectSchemaGenerationTest.php | 39 +- 16 files changed, 581 insertions(+), 348 deletions(-) diff --git a/src/Control/ContentNegotiator.php b/src/Control/ContentNegotiator.php index 8e5f621c3..aed1b0bfb 100644 --- a/src/Control/ContentNegotiator.php +++ b/src/Control/ContentNegotiator.php @@ -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) { diff --git a/src/Core/ClassInfo.php b/src/Core/ClassInfo.php index 651f1bc09..bc3eaf30a 100644 --- a/src/Core/ClassInfo.php +++ b/src/Core/ClassInfo.php @@ -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: * * ClassInfo::subclassesFor('BaseClass'); * array( - * 'BaseClass' => 'BaseClass', - * 'ChildClass' => 'ChildClass', - * 'GrandChildClass' => 'GrandChildClass' + * 'baseclass' => 'BaseClass', + * 'childclass' => 'ChildClass', + * 'grandchildclass' => 'GrandChildClass' * ) * * * @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); } /** diff --git a/src/Core/Config/CoreConfigFactory.php b/src/Core/Config/CoreConfigFactory.php index 88e1343c3..509f17a29 100644 --- a/src/Core/Config/CoreConfigFactory.php +++ b/src/Core/Config/CoreConfigFactory.php @@ -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(); }); } diff --git a/src/Core/Config/Middleware/InheritanceMiddleware.php b/src/Core/Config/Middleware/InheritanceMiddleware.php index 526724595..80654771b 100644 --- a/src/Core/Config/Middleware/InheritanceMiddleware.php +++ b/src/Core/Config/Middleware/InheritanceMiddleware.php @@ -31,6 +31,7 @@ class InheritanceMiddleware implements Middleware $nextConfig = $next($nextClass, $excludeMiddleware); $config = Priority::mergeArray($nextConfig, $config); } + return $config; } } diff --git a/src/Core/Manifest/ClassManifest.php b/src/Core/Manifest/ClassManifest.php index 2a48d4ee5..64ec024d3 100644 --- a/src/Core/Manifest/ClassManifest.php +++ b/src/Core/Manifest/ClassManifest.php @@ -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])) { diff --git a/src/Core/Manifest/ModuleManifest.php b/src/Core/Manifest/ModuleManifest.php index c208868a5..8ccce6d88 100644 --- a/src/Core/Manifest/ModuleManifest.php +++ b/src/Core/Manifest/ModuleManifest.php @@ -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', diff --git a/src/Dev/TaskRunner.php b/src/Dev/TaskRunner.php index 1b5e2eb9b..cd38ff565 100644 --- a/src/Dev/TaskRunner.php +++ b/src/Dev/TaskRunner.php @@ -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); diff --git a/src/ORM/DatabaseAdmin.php b/src/ORM/DatabaseAdmin.php index 54a068827..0489099a7 100644 --- a/src/ORM/DatabaseAdmin.php +++ b/src/ORM/DatabaseAdmin.php @@ -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) { diff --git a/src/ORM/FieldType/DBClassName.php b/src/ORM/FieldType/DBClassName.php index 94bb2bffa..a2f381f4c 100644 --- a/src/ORM/FieldType/DBClassName.php +++ b/src/ORM/FieldType/DBClassName.php @@ -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); } /** diff --git a/src/ORM/PolymorphicHasManyList.php b/src/ORM/PolymorphicHasManyList.php index 83cb2ec69..7bfb9f0d8 100644 --- a/src/ORM/PolymorphicHasManyList.php +++ b/src/ORM/PolymorphicHasManyList.php @@ -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; } diff --git a/tests/php/Core/ClassInfoTest.php b/tests/php/Core/ClassInfoTest.php index fb4d41ec4..e45a062ea 100644 --- a/tests/php/Core/ClassInfoTest.php +++ b/tests/php/Core/ClassInfoTest.php @@ -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])); diff --git a/tests/php/Core/Manifest/ClassManifestTest.php b/tests/php/Core/Manifest/ClassManifestTest.php index f1549ca5c..6629e1735 100644 --- a/tests/php/Core/Manifest/ClassManifestTest.php +++ b/tests/php/Core/Manifest/ClassManifestTest.php @@ -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()); } /** diff --git a/tests/php/Core/Manifest/NamespacedClassManifestTest.php b/tests/php/Core/Manifest/NamespacedClassManifestTest.php index 83c5d36eb..ef48eae1a 100644 --- a/tests/php/Core/Manifest/NamespacedClassManifestTest.php +++ b/tests/php/Core/Manifest/NamespacedClassManifestTest.php @@ -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)); diff --git a/tests/php/ORM/DBClassNameTest.php b/tests/php/ORM/DBClassNameTest.php index 3b8c77f05..91bad680b 100644 --- a/tests/php/ORM/DBClassNameTest.php +++ b/tests/php/ORM/DBClassNameTest.php @@ -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() ); } diff --git a/tests/php/ORM/DataExtensionTest.php b/tests/php/ORM/DataExtensionTest.php index 7d95aa0fd..62bc5df66 100644 --- a/tests/php/ORM/DataExtensionTest.php +++ b/tests/php/ORM/DataExtensionTest.php @@ -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')); diff --git a/tests/php/ORM/DataObjectSchemaGenerationTest.php b/tests/php/ORM/DataObjectSchemaGenerationTest.php index 27440d85d..3f305b824 100644 --- a/tests/php/ORM/DataObjectSchemaGenerationTest.php +++ b/tests/php/ORM/DataObjectSchemaGenerationTest.php @@ -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();