mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
ENHANCEMENT Don't force all class names to lowercase
Speeds up autoloading because composer psr-4 works properly now
This commit is contained in:
parent
9a7adc46f8
commit
261302a121
@ -116,7 +116,7 @@ class ContentNegotiator
|
|||||||
$chosenFormat = "xhtml";
|
$chosenFormat = "xhtml";
|
||||||
} else {
|
} else {
|
||||||
foreach ($mimes as $format => $mime) {
|
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)) {
|
if (isset($_SERVER['HTTP_ACCEPT']) && preg_match($regExp, $_SERVER['HTTP_ACCEPT'], $matches)) {
|
||||||
$preference = isset($matches[2]) ? $matches[2] : 1;
|
$preference = isset($matches[2]) ? $matches[2] : 1;
|
||||||
if (!isset($q[$preference])) {
|
if (!isset($q[$preference])) {
|
||||||
@ -130,13 +130,13 @@ class ContentNegotiator
|
|||||||
krsort($q);
|
krsort($q);
|
||||||
$chosenFormat = reset($q);
|
$chosenFormat = reset($q);
|
||||||
} else {
|
} else {
|
||||||
$chosenFormat = Config::inst()->get('SilverStripe\\Control\\ContentNegotiator', 'default_format');
|
$chosenFormat = Config::inst()->get(static::class, 'default_format');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$negotiator = new ContentNegotiator();
|
$negotiator = new ContentNegotiator();
|
||||||
$negotiator->$chosenFormat( $response );
|
$negotiator->$chosenFormat($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,7 +202,7 @@ class ContentNegotiator
|
|||||||
$response->addHeader("Vary", "Accept");
|
$response->addHeader("Vary", "Accept");
|
||||||
|
|
||||||
$content = $response->getBody();
|
$content = $response->getBody();
|
||||||
$hasXMLHeader = (substr($content, 0, 5) == '<' . '?xml' );
|
$hasXMLHeader = (substr($content, 0, 5) == '<' . '?xml');
|
||||||
|
|
||||||
// Fix base tag
|
// Fix base tag
|
||||||
$content = preg_replace(
|
$content = preg_replace(
|
||||||
@ -212,7 +212,11 @@ class ContentNegotiator
|
|||||||
);
|
);
|
||||||
|
|
||||||
$content = preg_replace("#<\\?xml[^>]+\\?>\n?#", '', $content);
|
$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
|
// Only replace the doctype in templates with the xml header
|
||||||
if ($hasXMLHeader) {
|
if ($hasXMLHeader) {
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
namespace SilverStripe\Core;
|
namespace SilverStripe\Core;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use ReflectionClass;
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Core\Manifest\ClassLoader;
|
use SilverStripe\Core\Manifest\ClassLoader;
|
||||||
use SilverStripe\Dev\Deprecation;
|
use SilverStripe\Dev\Deprecation;
|
||||||
use SilverStripe\ORM\ArrayLib;
|
|
||||||
use SilverStripe\ORM\DB;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
use ReflectionClass;
|
use SilverStripe\ORM\DB;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides introspection information about the class tree.
|
* Provides introspection information about the class tree.
|
||||||
@ -20,28 +20,6 @@ use ReflectionClass;
|
|||||||
*/
|
*/
|
||||||
class ClassInfo
|
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()}
|
* Cache for {@link hasTable()}
|
||||||
*
|
*
|
||||||
@ -64,6 +42,14 @@ class ClassInfo
|
|||||||
*/
|
*/
|
||||||
private static $_cache_parse = [];
|
private static $_cache_parse = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for has_method_from
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $_cache_methods = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache for class_name
|
* Cache for class_name
|
||||||
*
|
*
|
||||||
@ -72,6 +58,29 @@ class ClassInfo
|
|||||||
*/
|
*/
|
||||||
private static $_cache_class_names = [];
|
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
|
* @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.
|
* types that don't exist as implemented classes. By default these are excluded.
|
||||||
* @return array List of subclasses
|
* @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)) {
|
if (is_string($class) && !class_exists($class)) {
|
||||||
return array();
|
return array();
|
||||||
@ -129,29 +138,26 @@ class ClassInfo
|
|||||||
public static function dataClassesFor($nameOrObject)
|
public static function dataClassesFor($nameOrObject)
|
||||||
{
|
{
|
||||||
if (is_string($nameOrObject) && !class_exists($nameOrObject)) {
|
if (is_string($nameOrObject) && !class_exists($nameOrObject)) {
|
||||||
return array();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = array();
|
// Get all classes
|
||||||
|
|
||||||
$class = self::class_name($nameOrObject);
|
$class = self::class_name($nameOrObject);
|
||||||
|
|
||||||
$classes = array_merge(
|
$classes = array_merge(
|
||||||
self::ancestry($class),
|
self::ancestry($class),
|
||||||
self::subclassesFor($class)
|
self::subclassesFor($class)
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($classes as $class) {
|
// Filter by table
|
||||||
if (DataObject::getSchema()->classHasTable($class)) {
|
return array_filter($classes, function ($next) {
|
||||||
$result[$class] = $class;
|
return DataObject::getSchema()->classHasTable($next);
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated 4.0..5.0
|
* @deprecated 4.0..5.0
|
||||||
|
* @param string $class
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function baseDataClass($class)
|
public static function baseDataClass($class)
|
||||||
{
|
{
|
||||||
@ -163,19 +169,20 @@ class ClassInfo
|
|||||||
* Returns a list of classes that inherit from the given class.
|
* Returns a list of classes that inherit from the given class.
|
||||||
* The resulting array includes the base class passed
|
* The resulting array includes the base class passed
|
||||||
* through the $class parameter as the first array value.
|
* through the $class parameter as the first array value.
|
||||||
|
* Note that keys are lowercase, while the values are correct case.
|
||||||
*
|
*
|
||||||
* Example usage:
|
* Example usage:
|
||||||
* <code>
|
* <code>
|
||||||
* ClassInfo::subclassesFor('BaseClass');
|
* ClassInfo::subclassesFor('BaseClass');
|
||||||
* array(
|
* array(
|
||||||
* 'BaseClass' => 'BaseClass',
|
* 'baseclass' => 'BaseClass',
|
||||||
* 'ChildClass' => 'ChildClass',
|
* 'childclass' => 'ChildClass',
|
||||||
* 'GrandChildClass' => 'GrandChildClass'
|
* 'grandchildclass' => 'GrandChildClass'
|
||||||
* )
|
* )
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* @param string|object $nameOrObject The classname or object
|
* @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)
|
public static function subclassesFor($nameOrObject)
|
||||||
{
|
{
|
||||||
@ -183,16 +190,16 @@ class ClassInfo
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
//normalise class case
|
// Get class names
|
||||||
$className = self::class_name($nameOrObject);
|
$className = self::class_name($nameOrObject);
|
||||||
$descendants = ClassLoader::inst()->getManifest()->getDescendantsOf($className);
|
$lowerClassName = strtolower($className);
|
||||||
$result = array($className => $className);
|
|
||||||
|
|
||||||
if ($descendants) {
|
// Merge with descendants
|
||||||
return $result + ArrayLib::valuekey($descendants);
|
$descendants = ClassLoader::inst()->getManifest()->getDescendantsOf($className);
|
||||||
} else {
|
return array_merge(
|
||||||
return $result;
|
[ $lowerClassName => $className ],
|
||||||
}
|
$descendants
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -211,8 +218,15 @@ class ClassInfo
|
|||||||
|
|
||||||
$key = strtolower($nameOrObject);
|
$key = strtolower($nameOrObject);
|
||||||
if (!isset(static::$_cache_class_names[$key])) {
|
if (!isset(static::$_cache_class_names[$key])) {
|
||||||
$reflection = new ReflectionClass($nameOrObject);
|
// Get manifest name
|
||||||
static::$_cache_class_names[$key] = $reflection->getName();
|
$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];
|
return static::$_cache_class_names[$key];
|
||||||
@ -224,25 +238,25 @@ class ClassInfo
|
|||||||
*
|
*
|
||||||
* @param string|object $nameOrObject Class or object instance
|
* @param string|object $nameOrObject Class or object instance
|
||||||
* @param bool $tablesOnly Only return classes that have a table in the db.
|
* @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)
|
public static function ancestry($nameOrObject, $tablesOnly = false)
|
||||||
{
|
{
|
||||||
if (is_string($nameOrObject) && !class_exists($nameOrObject)) {
|
if (is_string($nameOrObject) && !class_exists($nameOrObject)) {
|
||||||
return array();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$class = self::class_name($nameOrObject);
|
$class = self::class_name($nameOrObject);
|
||||||
|
|
||||||
$lClass = strtolower($class);
|
$lowerClass = strtolower($class);
|
||||||
|
|
||||||
$cacheKey = $lClass . '_' . (string)$tablesOnly;
|
$cacheKey = $lowerClass . '_' . (string)$tablesOnly;
|
||||||
$parent = $class;
|
$parent = $class;
|
||||||
if (!isset(self::$_cache_ancestry[$cacheKey])) {
|
if (!isset(self::$_cache_ancestry[$cacheKey])) {
|
||||||
$ancestry = array();
|
$ancestry = [];
|
||||||
do {
|
do {
|
||||||
if (!$tablesOnly || DataObject::getSchema()->classHasTable($parent)) {
|
if (!$tablesOnly || DataObject::getSchema()->classHasTable($parent)) {
|
||||||
$ancestry[$parent] = $parent;
|
$ancestry[strtolower($parent)] = $parent;
|
||||||
}
|
}
|
||||||
} while ($parent = get_parent_class($parent));
|
} while ($parent = get_parent_class($parent));
|
||||||
self::$_cache_ancestry[$cacheKey] = array_reverse($ancestry);
|
self::$_cache_ancestry[$cacheKey] = array_reverse($ancestry);
|
||||||
@ -253,8 +267,8 @@ class ClassInfo
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $interfaceName
|
* @param string $interfaceName
|
||||||
* @return array A self-keyed array of class names. Note that this is only available with Silverstripe
|
* @return array A self-keyed array of class names with lowercase keys and correct-case values.
|
||||||
* classes and not built-in PHP classes.
|
* Note that this is only available with Silverstripe classes and not built-in PHP classes.
|
||||||
*/
|
*/
|
||||||
public static function implementorsOf($interfaceName)
|
public static function implementorsOf($interfaceName)
|
||||||
{
|
{
|
||||||
@ -270,28 +284,28 @@ class ClassInfo
|
|||||||
*/
|
*/
|
||||||
public static function classImplements($className, $interfaceName)
|
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.
|
* 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)
|
* @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)
|
public static function classes_for_file($filePath)
|
||||||
{
|
{
|
||||||
$absFilePath = Director::getAbsFile($filePath);
|
$absFilePath = Director::getAbsFile($filePath);
|
||||||
$matchedClasses = array();
|
$classManifest = ClassLoader::inst()->getManifest();
|
||||||
$manifest = ClassLoader::inst()->getManifest()->getClasses();
|
$classes = $classManifest->getClasses();
|
||||||
|
$classNames = $classManifest->getClassNames();
|
||||||
|
|
||||||
foreach ($manifest as $class => $compareFilePath) {
|
$matchedClasses = [];
|
||||||
if ($absFilePath == $compareFilePath) {
|
foreach ($classes as $lowerClass => $compareFilePath) {
|
||||||
$matchedClasses[] = $class;
|
if (strcasecmp($absFilePath, $compareFilePath) === 0) {
|
||||||
|
$matchedClasses[$lowerClass] = $classNames[$lowerClass];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,50 +315,55 @@ class ClassInfo
|
|||||||
/**
|
/**
|
||||||
* Returns all classes contained in a certain folder.
|
* 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
|
* @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)
|
public static function classes_for_folder($folderPath)
|
||||||
{
|
{
|
||||||
$absFolderPath = Director::getAbsFile($folderPath);
|
$absFolderPath = Director::getAbsFile($folderPath);
|
||||||
$matchedClasses = array();
|
$classManifest = ClassLoader::inst()->getManifest();
|
||||||
$manifest = ClassLoader::inst()->getManifest()->getClasses();
|
$classes = $classManifest->getClasses();
|
||||||
|
$classNames = $classManifest->getClassNames();
|
||||||
|
|
||||||
foreach ($manifest as $class => $compareFilePath) {
|
$matchedClasses = [];
|
||||||
|
foreach ($classes as $lowerClass => $compareFilePath) {
|
||||||
if (stripos($compareFilePath, $absFolderPath) === 0) {
|
if (stripos($compareFilePath, $absFolderPath) === 0) {
|
||||||
$matchedClasses[] = $class;
|
$matchedClasses[$lowerClass] = $classNames[$lowerClass];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $matchedClasses;
|
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)
|
public static function has_method_from($class, $method, $compclass)
|
||||||
{
|
{
|
||||||
$lClass = strtolower($class);
|
$lClass = strtolower($class);
|
||||||
$lMethod = strtolower($method);
|
$lMethod = strtolower($method);
|
||||||
$lCompclass = strtolower($compclass);
|
$lCompclass = strtolower($compclass);
|
||||||
if (!isset(self::$method_from_cache[$lClass])) {
|
if (!isset(self::$_cache_methods[$lClass])) {
|
||||||
self::$method_from_cache[$lClass] = array();
|
self::$_cache_methods[$lClass] = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!array_key_exists($lMethod, self::$method_from_cache[$lClass])) {
|
if (!array_key_exists($lMethod, self::$_cache_methods[$lClass])) {
|
||||||
self::$method_from_cache[$lClass][$lMethod] = false;
|
self::$_cache_methods[$lClass][$lMethod] = false;
|
||||||
|
|
||||||
$classRef = new ReflectionClass($class);
|
$classRef = new ReflectionClass($class);
|
||||||
|
|
||||||
if ($classRef->hasMethod($method)) {
|
if ($classRef->hasMethod($method)) {
|
||||||
$methodRef = $classRef->getMethod($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)
|
public static function shortName($nameOrObject)
|
||||||
{
|
{
|
||||||
$reflection = new ReflectionClass($nameOrObject);
|
$name = static::class_name($nameOrObject);
|
||||||
return $reflection->getShortName();
|
$parts = explode('\\', $name);
|
||||||
|
return end($parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,8 +112,9 @@ class CoreConfigFactory
|
|||||||
public function buildStaticTransformer()
|
public function buildStaticTransformer()
|
||||||
{
|
{
|
||||||
return new PrivateStaticTransformer(function () {
|
return new PrivateStaticTransformer(function () {
|
||||||
$classes = ClassLoader::inst()->getManifest()->getClasses();
|
return ClassLoader::inst()
|
||||||
return array_keys($classes);
|
->getManifest()
|
||||||
|
->getClassNames();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ class InheritanceMiddleware implements Middleware
|
|||||||
$nextConfig = $next($nextClass, $excludeMiddleware);
|
$nextConfig = $next($nextClass, $excludeMiddleware);
|
||||||
$config = Priority::mergeArray($nextConfig, $config);
|
$config = Priority::mergeArray($nextConfig, $config);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ use SilverStripe\Dev\TestOnly;
|
|||||||
* - Class and interface names and paths.
|
* - Class and interface names and paths.
|
||||||
* - All direct and indirect descendants of a class.
|
* - All direct and indirect descendants of a class.
|
||||||
* - All implementors of an interface.
|
* - All implementors of an interface.
|
||||||
|
*
|
||||||
|
* To be consistent; In general all array keys are lowercase, and array values are correct-case
|
||||||
*/
|
*/
|
||||||
class ClassManifest
|
class ClassManifest
|
||||||
{
|
{
|
||||||
@ -51,21 +53,51 @@ class ClassManifest
|
|||||||
protected $cacheKey;
|
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
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $classes = 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
|
* List of root classes with no parent class
|
||||||
|
* Keys are lowercase, values are correct case.
|
||||||
|
*
|
||||||
|
* Note: Only used while regenerating cache
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $roots = 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
|
* @var array
|
||||||
*/
|
*/
|
||||||
@ -73,31 +105,49 @@ class ClassManifest
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* List of descendents for any class (direct + indirect children)
|
* 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
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $descendants = array();
|
protected $descendants = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of interfaces and paths to those files
|
* Map of lowercase interface name to path those files
|
||||||
*
|
*
|
||||||
* @var array
|
* @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
|
* 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
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $implementors = array();
|
protected $implementors = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of traits to paths
|
* Map of lowercase trait names to paths
|
||||||
*
|
*
|
||||||
* @var array
|
* @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
|
* PHP Parser for parsing found files
|
||||||
@ -141,20 +191,22 @@ class ClassManifest
|
|||||||
// build cache from factory
|
// build cache from factory
|
||||||
if ($this->cacheFactory) {
|
if ($this->cacheFactory) {
|
||||||
$this->cache = $this->cacheFactory->create(
|
$this->cache = $this->cacheFactory->create(
|
||||||
CacheInterface::class.'.classmanifest',
|
CacheInterface::class . '.classmanifest',
|
||||||
[ 'namespace' => 'classmanifest' . ($includeTests ? '_tests' : '') ]
|
['namespace' => 'classmanifest' . ($includeTests ? '_tests' : '')]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$forceRegen && $this->cache && ($data = $this->cache->get($this->cacheKey))) {
|
// Check if cache is safe to use
|
||||||
$this->classes = $data['classes'];
|
if (!$forceRegen
|
||||||
$this->descendants = $data['descendants'];
|
&& $this->cache
|
||||||
$this->interfaces = $data['interfaces'];
|
&& ($data = $this->cache->get($this->cacheKey))
|
||||||
$this->implementors = $data['implementors'];
|
&& $this->loadState($data)
|
||||||
$this->traits = $data['traits'];
|
) {
|
||||||
} else {
|
return;
|
||||||
$this->regenerate($includeTests);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build
|
||||||
|
$this->regenerate($includeTests);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,6 +223,11 @@ class ClassManifest
|
|||||||
return $this->parser;
|
return $this->parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get node traverser for parsing class files
|
||||||
|
*
|
||||||
|
* @return NodeTraverser
|
||||||
|
*/
|
||||||
public function getTraverser()
|
public function getTraverser()
|
||||||
{
|
{
|
||||||
if (!$this->traverser) {
|
if (!$this->traverser) {
|
||||||
@ -182,6 +239,11 @@ class ClassManifest
|
|||||||
return $this->traverser;
|
return $this->traverser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get visitor for parsing class files
|
||||||
|
*
|
||||||
|
* @return ClassManifestVisitor
|
||||||
|
*/
|
||||||
public function getVisitor()
|
public function getVisitor()
|
||||||
{
|
{
|
||||||
if (!$this->visitor) {
|
if (!$this->visitor) {
|
||||||
@ -200,15 +262,35 @@ class ClassManifest
|
|||||||
*/
|
*/
|
||||||
public function getItemPath($name)
|
public function getItemPath($name)
|
||||||
{
|
{
|
||||||
$name = strtolower($name);
|
$lowerName = strtolower($name);
|
||||||
|
|
||||||
foreach ([
|
foreach ([
|
||||||
$this->classes,
|
$this->classes,
|
||||||
$this->interfaces,
|
$this->interfaces,
|
||||||
$this->traits
|
$this->traits,
|
||||||
] as $source) {
|
] as $source) {
|
||||||
if (isset($source[$name]) && file_exists($source[$name])) {
|
if (isset($source[$lowerName]) && file_exists($source[$lowerName])) {
|
||||||
return $source[$name];
|
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;
|
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
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getClassNames()
|
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
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getTraitNames()
|
public function getTraitNames()
|
||||||
{
|
{
|
||||||
return array_keys($this->traits);
|
return $this->traitNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -268,12 +360,11 @@ class ClassManifest
|
|||||||
}
|
}
|
||||||
|
|
||||||
$lClass = strtolower($class);
|
$lClass = strtolower($class);
|
||||||
|
|
||||||
if (array_key_exists($lClass, $this->descendants)) {
|
if (array_key_exists($lClass, $this->descendants)) {
|
||||||
return $this->descendants[$lClass];
|
return $this->descendants[$lClass];
|
||||||
} else {
|
|
||||||
return array();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -286,6 +377,16 @@ class ClassManifest
|
|||||||
return $this->interfaces;
|
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
|
* Returns a map of lowercased interface names to the classes the implement
|
||||||
* them.
|
* them.
|
||||||
@ -301,15 +402,14 @@ class ClassManifest
|
|||||||
* Returns an array containing the class names that implement a certain
|
* Returns an array containing the class names that implement a certain
|
||||||
* interface.
|
* interface.
|
||||||
*
|
*
|
||||||
* @param string $interface
|
* @param string $interface
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getImplementorsOf($interface)
|
public function getImplementorsOf($interface)
|
||||||
{
|
{
|
||||||
$interface = strtolower($interface);
|
$lowerInterface = strtolower($interface);
|
||||||
|
if (array_key_exists($lowerInterface, $this->implementors)) {
|
||||||
if (array_key_exists($interface, $this->implementors)) {
|
return $this->implementors[$lowerInterface];
|
||||||
return $this->implementors[$interface];
|
|
||||||
} else {
|
} else {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
@ -334,21 +434,16 @@ class ClassManifest
|
|||||||
*/
|
*/
|
||||||
public function regenerate($includeTests)
|
public function regenerate($includeTests)
|
||||||
{
|
{
|
||||||
$resets = array(
|
|
||||||
'classes', 'roots', 'children', 'descendants', 'interfaces',
|
|
||||||
'implementors', 'traits'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Reset the manifest so stale info doesn't cause errors.
|
// Reset the manifest so stale info doesn't cause errors.
|
||||||
foreach ($resets as $reset) {
|
$this->loadState([]);
|
||||||
$this->$reset = array();
|
$this->roots = [];
|
||||||
}
|
$this->children = [];
|
||||||
|
|
||||||
$finder = new ManifestFileFinder();
|
$finder = new ManifestFileFinder();
|
||||||
$finder->setOptions(array(
|
$finder->setOptions(array(
|
||||||
'name_regex' => '/^[^_].*\\.php$/',
|
'name_regex' => '/^[^_].*\\.php$/',
|
||||||
'ignore_files' => array('index.php', 'main.php', 'cli-script.php'),
|
'ignore_files' => array('index.php', 'main.php', 'cli-script.php'),
|
||||||
'ignore_tests' => !$includeTests,
|
'ignore_tests' => !$includeTests,
|
||||||
'file_callback' => function ($basename, $pathname) use ($includeTests) {
|
'file_callback' => function ($basename, $pathname) use ($includeTests) {
|
||||||
$this->handleFile($basename, $pathname, $includeTests);
|
$this->handleFile($basename, $pathname, $includeTests);
|
||||||
},
|
},
|
||||||
@ -360,23 +455,21 @@ class ClassManifest
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->cache) {
|
if ($this->cache) {
|
||||||
$data = array(
|
$data = $this->getState();
|
||||||
'classes' => $this->classes,
|
|
||||||
'descendants' => $this->descendants,
|
|
||||||
'interfaces' => $this->interfaces,
|
|
||||||
'implementors' => $this->implementors,
|
|
||||||
'traits' => $this->traits,
|
|
||||||
);
|
|
||||||
$this->cache->set($this->cacheKey, $data);
|
$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)
|
public function handleFile($basename, $pathname, $includeTests)
|
||||||
{
|
{
|
||||||
$classes = null;
|
|
||||||
$interfaces = null;
|
|
||||||
$traits = null;
|
|
||||||
|
|
||||||
// The results of individual file parses are cached, since only a few
|
// The results of individual file parses are cached, since only a few
|
||||||
// files will have changed and TokenisedRegularExpression is quite
|
// files will have changed and TokenisedRegularExpression is quite
|
||||||
// slow. A combination of the file name and file contents hash are used,
|
// 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);
|
$key = preg_replace('/[^a-zA-Z0-9_]/', '_', $basename) . '_' . md5_file($pathname);
|
||||||
|
|
||||||
// Attempt to load from cache
|
// Attempt to load from cache
|
||||||
|
// Note: $classes, $interfaces and $traits arrays have correct-case keys, not lowercase
|
||||||
$changed = false;
|
$changed = false;
|
||||||
if ($this->cache
|
if ($this->cache
|
||||||
&& ($data = $this->cache->get($key))
|
&& ($data = $this->cache->get($key))
|
||||||
@ -409,64 +503,64 @@ class ClassManifest
|
|||||||
$traits = $this->getVisitor()->getTraits();
|
$traits = $this->getVisitor()->getTraits();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge this data into the global list
|
// Merge raw class data into global list
|
||||||
foreach ($classes as $className => $classInfo) {
|
foreach ($classes as $className => $classInfo) {
|
||||||
$extends = !empty($classInfo['extends'])
|
$lowerClassName = strtolower($className);
|
||||||
? array_map('strtolower', $classInfo['extends'])
|
if (array_key_exists($lowerClassName, $this->classes)) {
|
||||||
: [];
|
|
||||||
$implements = !empty($classInfo['interfaces'])
|
|
||||||
? array_map('strtolower', $classInfo['interfaces'])
|
|
||||||
: [];
|
|
||||||
$lowercaseName = strtolower($className);
|
|
||||||
if (array_key_exists($lowercaseName, $this->classes)) {
|
|
||||||
throw new Exception(sprintf(
|
throw new Exception(sprintf(
|
||||||
'There are two files containing the "%s" class: "%s" and "%s"',
|
'There are two files containing the "%s" class: "%s" and "%s"',
|
||||||
$className,
|
$className,
|
||||||
$this->classes[$lowercaseName],
|
$this->classes[$lowerClassName],
|
||||||
$pathname
|
$pathname
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip if implements TestOnly, but doesn't include tests
|
// Skip if implements TestOnly, but doesn't include tests
|
||||||
if (!$includeTests
|
$lowerInterfaces = array_map('strtolower', $classInfo['interfaces']);
|
||||||
&& $implements
|
if (!$includeTests && in_array(strtolower(TestOnly::class), $lowerInterfaces)) {
|
||||||
&& in_array(strtolower(TestOnly::class), $implements)
|
|
||||||
) {
|
|
||||||
$changed = true;
|
$changed = true;
|
||||||
unset($classes[$className]);
|
unset($classes[$className]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->classes[$lowercaseName] = $pathname;
|
$this->classes[$lowerClassName] = $pathname;
|
||||||
|
$this->classNames[$lowerClassName] = $className;
|
||||||
|
|
||||||
if ($extends) {
|
// Add to children
|
||||||
foreach ($extends as $ancestor) {
|
if ($classInfo['extends']) {
|
||||||
if (!isset($this->children[$ancestor])) {
|
foreach ($classInfo['extends'] as $ancestor) {
|
||||||
$this->children[$ancestor] = array($className);
|
$lowerAncestor = strtolower($ancestor);
|
||||||
} else {
|
if (!isset($this->children[$lowerAncestor])) {
|
||||||
$this->children[$ancestor][] = $className;
|
$this->children[$lowerAncestor] = [];
|
||||||
}
|
}
|
||||||
|
$this->children[$lowerAncestor][$lowerClassName] = $className;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->roots[] = $className;
|
$this->roots[$lowerClassName] = $className;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($implements) {
|
// Load interfaces
|
||||||
foreach ($implements as $interface) {
|
foreach ($classInfo['interfaces'] as $interface) {
|
||||||
if (!isset($this->implementors[$interface])) {
|
$lowerInterface = strtolower($interface);
|
||||||
$this->implementors[$interface] = array($className);
|
if (!isset($this->implementors[$lowerInterface])) {
|
||||||
} else {
|
$this->implementors[$lowerInterface] = [];
|
||||||
$this->implementors[$interface][] = $className;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$this->implementors[$lowerInterface][$lowerClassName] = $className;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge all found interfaces into list
|
||||||
foreach ($interfaces as $interfaceName => $interfaceInfo) {
|
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) {
|
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
|
// Save back to cache if configured
|
||||||
@ -489,23 +583,57 @@ class ClassManifest
|
|||||||
*/
|
*/
|
||||||
protected function coalesceDescendants($class)
|
protected function coalesceDescendants($class)
|
||||||
{
|
{
|
||||||
$lClass = strtolower($class);
|
// Reset descendents to immediate children initially
|
||||||
|
$lowerClass = strtolower($class);
|
||||||
if (array_key_exists($lClass, $this->children)) {
|
if (empty($this->children[$lowerClass])) {
|
||||||
$this->descendants[$lClass] = array();
|
return [];
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
protected function validateItemCache($data)
|
||||||
{
|
{
|
||||||
|
if (!$data || !is_array($data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
foreach (['classes', 'interfaces', 'traits'] as $key) {
|
foreach (['classes', 'interfaces', 'traits'] as $key) {
|
||||||
// Must be set
|
// Must be set
|
||||||
if (!isset($data[$key])) {
|
if (!isset($data[$key])) {
|
||||||
|
@ -5,6 +5,7 @@ namespace SilverStripe\Core\Manifest;
|
|||||||
use LogicException;
|
use LogicException;
|
||||||
use Psr\SimpleCache\CacheInterface;
|
use Psr\SimpleCache\CacheInterface;
|
||||||
use SilverStripe\Core\Cache\CacheFactory;
|
use SilverStripe\Core\Cache\CacheFactory;
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Core\Config\Configurable;
|
use SilverStripe\Core\Config\Configurable;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
|
||||||
@ -50,6 +51,22 @@ class ModuleManifest
|
|||||||
*/
|
*/
|
||||||
protected $modules = [];
|
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
|
* Adds a path as a module
|
||||||
*
|
*
|
||||||
@ -244,6 +261,7 @@ class ModuleManifest
|
|||||||
{
|
{
|
||||||
$order = static::config()->uninherited('module_priority');
|
$order = static::config()->uninherited('module_priority');
|
||||||
$project = static::config()->get('project');
|
$project = static::config()->get('project');
|
||||||
|
|
||||||
/* @var PrioritySorter $sorter */
|
/* @var PrioritySorter $sorter */
|
||||||
$sorter = Injector::inst()->createWithArgs(
|
$sorter = Injector::inst()->createWithArgs(
|
||||||
PrioritySorter::class . '.modulesorter',
|
PrioritySorter::class . '.modulesorter',
|
||||||
|
@ -116,7 +116,7 @@ class TaskRunner extends Controller
|
|||||||
{
|
{
|
||||||
$availableTasks = array();
|
$availableTasks = array();
|
||||||
|
|
||||||
$taskClasses = ClassInfo::subclassesFor('SilverStripe\\Dev\\BuildTask');
|
$taskClasses = ClassInfo::subclassesFor(BuildTask::class);
|
||||||
// remove the base class
|
// remove the base class
|
||||||
array_shift($taskClasses);
|
array_shift($taskClasses);
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ class DatabaseAdmin extends Controller
|
|||||||
*/
|
*/
|
||||||
public function buildDefaults()
|
public function buildDefaults()
|
||||||
{
|
{
|
||||||
$dataClasses = ClassInfo::subclassesFor('SilverStripe\ORM\DataObject');
|
$dataClasses = ClassInfo::subclassesFor(DataObject::class);
|
||||||
array_shift($dataClasses);
|
array_shift($dataClasses);
|
||||||
|
|
||||||
if (!Director::is_cli()) {
|
if (!Director::is_cli()) {
|
||||||
@ -260,7 +260,7 @@ class DatabaseAdmin extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build the database. Most of the hard work is handled by DataObject
|
// 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);
|
array_shift($dataClasses);
|
||||||
|
|
||||||
if (!$quiet) {
|
if (!$quiet) {
|
||||||
|
@ -129,8 +129,9 @@ class DBClassName extends DBEnum
|
|||||||
public function getEnum()
|
public function getEnum()
|
||||||
{
|
{
|
||||||
$classNames = ClassInfo::subclassesFor($this->getBaseClass());
|
$classNames = ClassInfo::subclassesFor($this->getBaseClass());
|
||||||
unset($classNames[DataObject::class]);
|
$dataobject = strtolower(DataObject::class);
|
||||||
return $classNames;
|
unset($classNames[$dataobject]);
|
||||||
|
return array_values($classNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,9 +37,8 @@ class PolymorphicHasManyList extends HasManyList
|
|||||||
* to generate the ID and Class foreign keys.
|
* to generate the ID and Class foreign keys.
|
||||||
* @param string $foreignClass Name of the class filter this relation is filtered against
|
* @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
|
// Set both id foreign key (as in HasManyList) and the class foreign key
|
||||||
parent::__construct($dataClass, "{$foreignField}ID");
|
parent::__construct($dataClass, "{$foreignField}ID");
|
||||||
$this->classForeignKey = "{$foreignField}Class";
|
$this->classForeignKey = "{$foreignField}Class";
|
||||||
@ -120,7 +119,8 @@ class PolymorphicHasManyList extends HasManyList
|
|||||||
$foreignClass = $this->getForeignClass();
|
$foreignClass = $this->getForeignClass();
|
||||||
$classNames = ClassInfo::subclassesFor($foreignClass);
|
$classNames = ClassInfo::subclassesFor($foreignClass);
|
||||||
$classForeignKey = $this->classForeignKey;
|
$classForeignKey = $this->classForeignKey;
|
||||||
if (!in_array($item->$classForeignKey, $classNames)) {
|
$classValueLower = strtolower($item->$classForeignKey);
|
||||||
|
if (!array_key_exists($classValueLower, $classNames)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace SilverStripe\Core\Tests;
|
namespace SilverStripe\Core\Tests;
|
||||||
|
|
||||||
use ReflectionException;
|
use ReflectionException;
|
||||||
|
use SilverStripe\Core\ClassInfo;
|
||||||
use SilverStripe\Core\Tests\ClassInfoTest\BaseClass;
|
use SilverStripe\Core\Tests\ClassInfoTest\BaseClass;
|
||||||
use SilverStripe\Core\Tests\ClassInfoTest\BaseDataClass;
|
use SilverStripe\Core\Tests\ClassInfoTest\BaseDataClass;
|
||||||
use SilverStripe\Core\Tests\ClassInfoTest\ChildClass;
|
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\NoFields;
|
||||||
use SilverStripe\Core\Tests\ClassInfoTest\WithCustomTable;
|
use SilverStripe\Core\Tests\ClassInfoTest\WithCustomTable;
|
||||||
use SilverStripe\Core\Tests\ClassInfoTest\WithRelation;
|
use SilverStripe\Core\Tests\ClassInfoTest\WithRelation;
|
||||||
use SilverStripe\ORM\ArrayLib;
|
|
||||||
use SilverStripe\Core\ClassInfo;
|
|
||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\View\ViewableData;
|
use SilverStripe\View\ViewableData;
|
||||||
@ -50,22 +49,19 @@ class ClassInfoTest extends SapphireTest
|
|||||||
|
|
||||||
public function testSubclassesFor()
|
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(
|
$this->assertEquals(
|
||||||
array(
|
$subclasses,
|
||||||
BaseClass::class => BaseClass::class,
|
|
||||||
ChildClass::class => ChildClass::class,
|
|
||||||
GrandChildClass::class => GrandChildClass::class
|
|
||||||
),
|
|
||||||
ClassInfo::subclassesFor(BaseClass::class),
|
ClassInfo::subclassesFor(BaseClass::class),
|
||||||
'ClassInfo::subclassesFor() returns only direct subclasses and doesnt include base class'
|
'ClassInfo::subclassesFor() returns only direct subclasses and doesnt include base class'
|
||||||
);
|
);
|
||||||
ClassInfo::reset_db_cache();
|
ClassInfo::reset_db_cache();
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array(
|
$subclasses,
|
||||||
BaseClass::class => BaseClass::class,
|
|
||||||
ChildClass::class => ChildClass::class,
|
|
||||||
GrandChildClass::class => GrandChildClass::class
|
|
||||||
),
|
|
||||||
ClassInfo::subclassesFor('silverstripe\\core\\tests\\classinfotest\\baseclass'),
|
ClassInfo::subclassesFor('silverstripe\\core\\tests\\classinfotest\\baseclass'),
|
||||||
'ClassInfo::subclassesFor() is acting in a case sensitive way when it should not'
|
'ClassInfo::subclassesFor() is acting in a case sensitive way when it should not'
|
||||||
);
|
);
|
||||||
@ -96,20 +92,27 @@ class ClassInfoTest extends SapphireTest
|
|||||||
|
|
||||||
public function testClassesForFolder()
|
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', '/'));
|
$classes = ClassInfo::classes_for_folder(ltrim(FRAMEWORK_DIR . '/tests', '/'));
|
||||||
$this->assertContains(
|
$this->assertArrayHasKey(
|
||||||
'silverstripe\\core\\tests\\classinfotest',
|
'silverstripe\\core\\tests\\classinfotest',
|
||||||
$classes,
|
$classes,
|
||||||
'ClassInfo::classes_for_folder() returns classes matching the filename'
|
'ClassInfo::classes_for_folder() returns classes matching the filename'
|
||||||
);
|
);
|
||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
|
ClassInfoTest::class,
|
||||||
|
$classes,
|
||||||
|
'ClassInfo::classes_for_folder() returns classes matching the filename'
|
||||||
|
);
|
||||||
|
$this->assertArrayHasKey(
|
||||||
'silverstripe\\core\\tests\\classinfotest\\baseclass',
|
'silverstripe\\core\\tests\\classinfotest\\baseclass',
|
||||||
$classes,
|
$classes,
|
||||||
'ClassInfo::classes_for_folder() returns additional classes not matching the filename'
|
'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()
|
public function testAncestry()
|
||||||
{
|
{
|
||||||
$ancestry = ClassInfo::ancestry(ChildClass::class);
|
$ancestry = ClassInfo::ancestry(ChildClass::class);
|
||||||
$expect = ArrayLib::valuekey([
|
$expect = [
|
||||||
ViewableData::class,
|
'silverstripe\\view\\viewabledata' => ViewableData::class,
|
||||||
DataObject::class,
|
'silverstripe\\orm\\dataobject' => DataObject::class,
|
||||||
BaseClass::class,
|
'silverstripe\\core\tests\classinfotest\\baseclass' => BaseClass::class,
|
||||||
ChildClass::class,
|
'silverstripe\\core\tests\classinfotest\\childclass' => ChildClass::class,
|
||||||
]);
|
];
|
||||||
$this->assertEquals($expect, $ancestry);
|
$this->assertEquals($expect, $ancestry);
|
||||||
|
|
||||||
ClassInfo::reset_db_cache();
|
ClassInfo::reset_db_cache();
|
||||||
@ -135,7 +138,9 @@ class ClassInfoTest extends SapphireTest
|
|||||||
ClassInfo::reset_db_cache();
|
ClassInfo::reset_db_cache();
|
||||||
$ancestry = ClassInfo::ancestry(ChildClass::class, true);
|
$ancestry = ClassInfo::ancestry(ChildClass::class, true);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array(BaseClass::class => BaseClass::class),
|
[
|
||||||
|
'silverstripe\\core\tests\classinfotest\\baseclass' => BaseClass::class
|
||||||
|
],
|
||||||
$ancestry,
|
$ancestry,
|
||||||
'$tablesOnly option excludes memory-only inheritance classes'
|
'$tablesOnly option excludes memory-only inheritance classes'
|
||||||
);
|
);
|
||||||
@ -146,13 +151,12 @@ class ClassInfoTest extends SapphireTest
|
|||||||
*/
|
*/
|
||||||
public function testDataClassesFor()
|
public function testDataClassesFor()
|
||||||
{
|
{
|
||||||
$expect = array(
|
$expect = [
|
||||||
BaseDataClass::class => BaseDataClass::class,
|
'silverstripe\\core\\tests\\classinfotest\\basedataclass' => BaseDataClass::class,
|
||||||
HasFields::class => HasFields::class,
|
'silverstripe\\core\\tests\\classinfotest\\hasfields' => HasFields::class,
|
||||||
WithRelation::class => WithRelation::class,
|
'silverstripe\\core\\tests\\classinfotest\\withrelation' => WithRelation::class,
|
||||||
WithCustomTable::class => WithCustomTable::class,
|
'silverstripe\\core\\tests\\classinfotest\\withcustomtable' => WithCustomTable::class,
|
||||||
);
|
];
|
||||||
|
|
||||||
$classes = array(
|
$classes = array(
|
||||||
BaseDataClass::class,
|
BaseDataClass::class,
|
||||||
NoFields::class,
|
NoFields::class,
|
||||||
@ -166,10 +170,10 @@ class ClassInfoTest extends SapphireTest
|
|||||||
ClassInfo::reset_db_cache();
|
ClassInfo::reset_db_cache();
|
||||||
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[1]));
|
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[1]));
|
||||||
|
|
||||||
$expect = array(
|
$expect = [
|
||||||
BaseDataClass::class => BaseDataClass::class,
|
'silverstripe\\core\\tests\\classinfotest\\basedataclass' => BaseDataClass::class,
|
||||||
HasFields::class => HasFields::class,
|
'silverstripe\\core\\tests\\classinfotest\\hasfields' => HasFields::class,
|
||||||
);
|
];
|
||||||
|
|
||||||
ClassInfo::reset_db_cache();
|
ClassInfo::reset_db_cache();
|
||||||
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[2]));
|
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[2]));
|
||||||
|
@ -71,7 +71,13 @@ class ClassManifestTest extends SapphireTest
|
|||||||
public function testGetClassNames()
|
public function testGetClassNames()
|
||||||
{
|
{
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
['classa', 'classb', 'classc', 'classd', 'classe'],
|
[
|
||||||
|
'classa' => 'ClassA',
|
||||||
|
'classb' => 'ClassB',
|
||||||
|
'classc' => 'ClassC',
|
||||||
|
'classd' => 'ClassD',
|
||||||
|
'classe' => 'ClassE',
|
||||||
|
],
|
||||||
$this->manifest->getClassNames()
|
$this->manifest->getClassNames()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -79,28 +85,36 @@ class ClassManifestTest extends SapphireTest
|
|||||||
public function testGetTraitNames()
|
public function testGetTraitNames()
|
||||||
{
|
{
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array('testtraita', 'testnamespace\testing\testtraitb'),
|
array(
|
||||||
|
'testtraita' => 'TestTraitA',
|
||||||
|
'testnamespace\testing\testtraitb' => 'TestNamespace\Testing\TestTraitB',
|
||||||
|
),
|
||||||
$this->manifest->getTraitNames()
|
$this->manifest->getTraitNames()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetDescendants()
|
public function testGetDescendants()
|
||||||
{
|
{
|
||||||
$expect = array(
|
$expect = [
|
||||||
'classa' => array('ClassC', 'ClassD'),
|
'classa' => [
|
||||||
'classc' => array('ClassD')
|
'classc' => 'ClassC',
|
||||||
);
|
'classd' => 'ClassD',
|
||||||
|
],
|
||||||
|
'classc' => [
|
||||||
|
'classd' => 'ClassD',
|
||||||
|
],
|
||||||
|
];
|
||||||
$this->assertEquals($expect, $this->manifest->getDescendants());
|
$this->assertEquals($expect, $this->manifest->getDescendants());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetDescendantsOf()
|
public function testGetDescendantsOf()
|
||||||
{
|
{
|
||||||
$expect = array(
|
$expect = [
|
||||||
'CLASSA' => array('ClassC', 'ClassD'),
|
'CLASSA' => ['classc' => 'ClassC', 'classd' => 'ClassD'],
|
||||||
'classa' => array('ClassC', 'ClassD'),
|
'classa' => ['classc' => 'ClassC', 'classd' => 'ClassD'],
|
||||||
'CLASSC' => array('ClassD'),
|
'CLASSC' => ['classd' => 'ClassD'],
|
||||||
'classc' => array('ClassD')
|
'classc' => ['classd' => 'ClassD'],
|
||||||
);
|
];
|
||||||
|
|
||||||
foreach ($expect as $class => $desc) {
|
foreach ($expect as $class => $desc) {
|
||||||
$this->assertEquals($desc, $this->manifest->getDescendantsOf($class));
|
$this->assertEquals($desc, $this->manifest->getDescendantsOf($class));
|
||||||
@ -118,21 +132,21 @@ class ClassManifestTest extends SapphireTest
|
|||||||
|
|
||||||
public function testGetImplementors()
|
public function testGetImplementors()
|
||||||
{
|
{
|
||||||
$expect = array(
|
$expect = [
|
||||||
'interfacea' => array('ClassB'),
|
'interfacea' => ['classb' => 'ClassB'],
|
||||||
'interfaceb' => array('ClassC')
|
'interfaceb' => ['classc' => 'ClassC'],
|
||||||
);
|
];
|
||||||
$this->assertEquals($expect, $this->manifest->getImplementors());
|
$this->assertEquals($expect, $this->manifest->getImplementors());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetImplementorsOf()
|
public function testGetImplementorsOf()
|
||||||
{
|
{
|
||||||
$expect = array(
|
$expect = [
|
||||||
'INTERFACEA' => array('ClassB'),
|
'INTERFACEA' => ['classb' => 'ClassB'],
|
||||||
'interfacea' => array('ClassB'),
|
'interfacea' => ['classb' => 'ClassB'],
|
||||||
'INTERFACEB' => array('ClassC'),
|
'INTERFACEB' => ['classc' => 'ClassC'],
|
||||||
'interfaceb' => array('ClassC')
|
'interfaceb' => ['classc' => 'ClassC'],
|
||||||
);
|
];
|
||||||
|
|
||||||
foreach ($expect as $interface => $impl) {
|
foreach ($expect as $interface => $impl) {
|
||||||
$this->assertEquals($impl, $this->manifest->getImplementorsOf($interface));
|
$this->assertEquals($impl, $this->manifest->getImplementorsOf($interface));
|
||||||
@ -141,13 +155,13 @@ class ClassManifestTest extends SapphireTest
|
|||||||
|
|
||||||
public function testTestManifestIncludesTestClasses()
|
public function testTestManifestIncludesTestClasses()
|
||||||
{
|
{
|
||||||
$this->assertNotContains('testclassa', array_keys($this->manifest->getClasses()));
|
$this->assertArrayNotHasKey('testclassa', $this->manifest->getClasses());
|
||||||
$this->assertContains('testclassa', array_keys($this->manifestTests->getClasses()));
|
$this->assertArrayHasKey('testclassa', $this->manifestTests->getClasses());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testManifestExcludeFilesPrefixedWithUnderscore()
|
public function testManifestExcludeFilesPrefixedWithUnderscore()
|
||||||
{
|
{
|
||||||
$this->assertNotContains('ignore', array_keys($this->manifest->getClasses()));
|
$this->assertArrayNotHasKey('ignore', $this->manifest->getClasses());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
namespace SilverStripe\Core\Tests\Manifest;
|
namespace SilverStripe\Core\Tests\Manifest;
|
||||||
|
|
||||||
|
use SilverStripe\Admin\ModelAdmin;
|
||||||
use SilverStripe\Core\ClassInfo;
|
use SilverStripe\Core\ClassInfo;
|
||||||
use SilverStripe\Core\Manifest\ClassManifest;
|
use SilverStripe\Core\Manifest\ClassManifest;
|
||||||
use SilverStripe\Core\Manifest\ClassLoader;
|
use SilverStripe\Core\Manifest\ClassLoader;
|
||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
use ReflectionMethod;
|
use ReflectionMethod;
|
||||||
|
use SilverStripe\Security\PermissionProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for the {@link ClassManifest} class.
|
* Tests for the {@link ClassManifest} class.
|
||||||
@ -41,27 +43,29 @@ class NamespacedClassManifestTest extends SapphireTest
|
|||||||
|
|
||||||
public function testClassInfoIsCorrect()
|
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
|
// 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
|
// descendants of the core classes we want to test against - this is a limitation of the test manifest not
|
||||||
// including all core classes
|
// including all core classes
|
||||||
$method = new ReflectionMethod($this->manifest, 'coalesceDescendants');
|
$method = new ReflectionMethod($this->manifest, 'coalesceDescendants');
|
||||||
$method->setAccessible(true);
|
$method->setAccessible(true);
|
||||||
$method->invoke($this->manifest, 'SilverStripe\\Admin\\ModelAdmin');
|
$method->invoke($this->manifest, ModelAdmin::class);
|
||||||
|
$this->assertContains('SilverStripe\\Framework\\Tests\\ClassI', ClassInfo::subclassesFor(ModelAdmin::class));
|
||||||
$this->assertContains('SilverStripe\Framework\Tests\ClassI', ClassInfo::subclassesFor('SilverStripe\\Admin\\ModelAdmin'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetItemPath()
|
public function testGetItemPath()
|
||||||
{
|
{
|
||||||
$expect = array(
|
$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\\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\\InterfaceA' => 'module/interfaces/InterfaceA.php',
|
||||||
'silverstripe\test\interfacea' => 'module/interfaces/InterfaceA.php'
|
'silverstripe\\test\\interfacea' => 'module/interfaces/InterfaceA.php'
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($expect as $name => $path) {
|
foreach ($expect as $name => $path) {
|
||||||
@ -72,15 +76,15 @@ class NamespacedClassManifestTest extends SapphireTest
|
|||||||
public function testGetClasses()
|
public function testGetClasses()
|
||||||
{
|
{
|
||||||
$expect = array(
|
$expect = array(
|
||||||
'silverstripe\test\classa' => "{$this->base}/module/classes/ClassA.php",
|
'silverstripe\\test\\classa' => "{$this->base}/module/classes/ClassA.php",
|
||||||
'silverstripe\test\classb' => "{$this->base}/module/classes/ClassB.php",
|
'silverstripe\\test\\classb' => "{$this->base}/module/classes/ClassB.php",
|
||||||
'silverstripe\test\classc' => "{$this->base}/module/classes/ClassC.php",
|
'silverstripe\\test\\classc' => "{$this->base}/module/classes/ClassC.php",
|
||||||
'silverstripe\test\classd' => "{$this->base}/module/classes/ClassD.php",
|
'silverstripe\\test\\classd' => "{$this->base}/module/classes/ClassD.php",
|
||||||
'silverstripe\test\classe' => "{$this->base}/module/classes/ClassE.php",
|
'silverstripe\\test\\classe' => "{$this->base}/module/classes/ClassE.php",
|
||||||
'silverstripe\test\classf' => "{$this->base}/module/classes/ClassF.php",
|
'silverstripe\\test\\classf' => "{$this->base}/module/classes/ClassF.php",
|
||||||
'silverstripe\test\classg' => "{$this->base}/module/classes/ClassG.php",
|
'silverstripe\\test\\classg' => "{$this->base}/module/classes/ClassG.php",
|
||||||
'silverstripe\test\classh' => "{$this->base}/module/classes/ClassH.php",
|
'silverstripe\\test\\classh' => "{$this->base}/module/classes/ClassH.php",
|
||||||
'silverstripe\framework\tests\classi' => "{$this->base}/module/classes/ClassI.php",
|
'silverstripe\\framework\\tests\\classi' => "{$this->base}/module/classes/ClassI.php",
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals($expect, $this->manifest->getClasses());
|
$this->assertEquals($expect, $this->manifest->getClasses());
|
||||||
@ -89,29 +93,45 @@ class NamespacedClassManifestTest extends SapphireTest
|
|||||||
public function testGetClassNames()
|
public function testGetClassNames()
|
||||||
{
|
{
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array('silverstripe\test\classa',
|
[
|
||||||
'silverstripe\test\classb', 'silverstripe\test\classc', 'silverstripe\test\classd',
|
'silverstripe\\test\\classa' => 'silverstripe\\test\\ClassA',
|
||||||
'silverstripe\test\classe', 'silverstripe\test\classf', 'silverstripe\test\classg',
|
'silverstripe\\test\\classb' => 'silverstripe\\test\\ClassB',
|
||||||
'silverstripe\test\classh', 'silverstripe\framework\tests\classi'),
|
'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()
|
$this->manifest->getClassNames()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetDescendants()
|
public function testGetDescendants()
|
||||||
{
|
{
|
||||||
$expect = array(
|
$expect = [
|
||||||
'silverstripe\test\classa' => array('silverstripe\test\ClassB', 'silverstripe\test\ClassH'),
|
'silverstripe\\test\\classa' => [
|
||||||
);
|
'silverstripe\\test\\classb' => 'silverstripe\test\ClassB',
|
||||||
|
'silverstripe\\test\\classh' => 'silverstripe\test\ClassH',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
$this->assertEquals($expect, $this->manifest->getDescendants());
|
$this->assertEquals($expect, $this->manifest->getDescendants());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetDescendantsOf()
|
public function testGetDescendantsOf()
|
||||||
{
|
{
|
||||||
$expect = array(
|
$expect = [
|
||||||
'SILVERSTRIPE\TEST\CLASSA' => array('silverstripe\test\ClassB', 'silverstripe\test\ClassH'),
|
'SILVERSTRIPE\\TEST\\CLASSA' => [
|
||||||
'silverstripe\test\classa' => array('silverstripe\test\ClassB', 'silverstripe\test\ClassH'),
|
'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) {
|
foreach ($expect as $class => $desc) {
|
||||||
$this->assertEquals($desc, $this->manifest->getDescendantsOf($class));
|
$this->assertEquals($desc, $this->manifest->getDescendantsOf($class));
|
||||||
@ -121,32 +141,52 @@ class NamespacedClassManifestTest extends SapphireTest
|
|||||||
public function testGetInterfaces()
|
public function testGetInterfaces()
|
||||||
{
|
{
|
||||||
$expect = array(
|
$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());
|
$this->assertEquals($expect, $this->manifest->getInterfaces());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetImplementors()
|
public function testGetImplementors()
|
||||||
{
|
{
|
||||||
$expect = array(
|
$expect = [
|
||||||
'silverstripe\test\interfacea' => array('silverstripe\test\ClassE'),
|
'silverstripe\\test\\interfacea' => [
|
||||||
'interfacea' => array('silverstripe\test\ClassF'),
|
'silverstripe\\test\\classe' => 'silverstripe\\test\\ClassE',
|
||||||
'silverstripe\test\subtest\interfacea' => array('silverstripe\test\ClassG'),
|
],
|
||||||
'silverstripe\security\permissionprovider' => array('SilverStripe\Framework\Tests\ClassI'),
|
'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());
|
$this->assertEquals($expect, $this->manifest->getImplementors());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetImplementorsOf()
|
public function testGetImplementorsOf()
|
||||||
{
|
{
|
||||||
$expect = array(
|
$expect = [
|
||||||
'SILVERSTRIPE\TEST\INTERFACEA' => array('silverstripe\test\ClassE'),
|
'SILVERSTRIPE\\TEST\\INTERFACEA' => [
|
||||||
'silverstripe\test\interfacea' => array('silverstripe\test\ClassE'),
|
'silverstripe\\test\\classe' => 'silverstripe\\test\\ClassE',
|
||||||
'INTERFACEA' => array('silverstripe\test\ClassF'),
|
],
|
||||||
'interfacea' => array('silverstripe\test\ClassF'),
|
'silverstripe\\test\\interfacea' => [
|
||||||
'SILVERSTRIPE\TEST\SUBTEST\INTERFACEA' => array('silverstripe\test\ClassG'),
|
'silverstripe\\test\\classe' => 'silverstripe\\test\\ClassE',
|
||||||
'silverstripe\test\subtest\interfacea' => array('silverstripe\test\ClassG'),
|
],
|
||||||
);
|
'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) {
|
foreach ($expect as $interface => $impl) {
|
||||||
$this->assertEquals($impl, $this->manifest->getImplementorsOf($interface));
|
$this->assertEquals($impl, $this->manifest->getImplementorsOf($interface));
|
||||||
|
@ -28,21 +28,27 @@ class DBClassNameTest extends SapphireTest
|
|||||||
{
|
{
|
||||||
// Object 1 fields
|
// Object 1 fields
|
||||||
$object = new DBClassNameTest\TestObject();
|
$object = new DBClassNameTest\TestObject();
|
||||||
|
/** @var DBClassName $defaultClass */
|
||||||
$defaultClass = $object->dbObject('DefaultClass');
|
$defaultClass = $object->dbObject('DefaultClass');
|
||||||
|
/** @var DBClassName $anyClass */
|
||||||
$anyClass = $object->dbObject('AnyClass');
|
$anyClass = $object->dbObject('AnyClass');
|
||||||
|
/** @var DBClassName $childClass */
|
||||||
$childClass = $object->dbObject('ChildClass');
|
$childClass = $object->dbObject('ChildClass');
|
||||||
|
/** @var DBClassName $leafClass */
|
||||||
$leafClass = $object->dbObject('LeafClass');
|
$leafClass = $object->dbObject('LeafClass');
|
||||||
|
|
||||||
// Object 2 fields
|
// Object 2 fields
|
||||||
$object2 = new DBClassNameTest\ObjectSubClass();
|
$object2 = new DBClassNameTest\ObjectSubClass();
|
||||||
|
/** @var DBClassName $midDefault */
|
||||||
$midDefault = $object2->dbObject('MidClassDefault');
|
$midDefault = $object2->dbObject('MidClassDefault');
|
||||||
|
/** @var DBClassName $midClass */
|
||||||
$midClass = $object2->dbObject('MidClass');
|
$midClass = $object2->dbObject('MidClass');
|
||||||
|
|
||||||
// Default fields always default to children of base class (even if put in a subclass)
|
// Default fields always default to children of base class (even if put in a subclass)
|
||||||
$mainSubclasses = array (
|
$mainSubclasses = array (
|
||||||
DBClassNameTest\TestObject::class => DBClassNameTest\TestObject::class,
|
DBClassNameTest\TestObject::class,
|
||||||
DBClassNameTest\ObjectSubClass::class => DBClassNameTest\ObjectSubClass::class,
|
DBClassNameTest\ObjectSubClass::class,
|
||||||
DBClassNameTest\ObjectSubSubClass::class => DBClassNameTest\ObjectSubSubClass::class,
|
DBClassNameTest\ObjectSubSubClass::class,
|
||||||
);
|
);
|
||||||
$this->assertEquals($mainSubclasses, $defaultClass->getEnumObsolete());
|
$this->assertEquals($mainSubclasses, $defaultClass->getEnumObsolete());
|
||||||
$this->assertEquals($mainSubclasses, $midDefault->getEnumObsolete());
|
$this->assertEquals($mainSubclasses, $midDefault->getEnumObsolete());
|
||||||
@ -56,15 +62,15 @@ class DBClassNameTest extends SapphireTest
|
|||||||
|
|
||||||
// Classes bound to the middle of a tree
|
// Classes bound to the middle of a tree
|
||||||
$midSubClasses = $mainSubclasses = array (
|
$midSubClasses = $mainSubclasses = array (
|
||||||
DBClassNameTest\ObjectSubClass::class => DBClassNameTest\ObjectSubClass::class,
|
DBClassNameTest\ObjectSubClass::class,
|
||||||
DBClassNameTest\ObjectSubSubClass::class => DBClassNameTest\ObjectSubSubClass::class,
|
DBClassNameTest\ObjectSubSubClass::class,
|
||||||
);
|
);
|
||||||
$this->assertEquals($midSubClasses, $childClass->getEnumObsolete());
|
$this->assertEquals($midSubClasses, $childClass->getEnumObsolete());
|
||||||
$this->assertEquals($midSubClasses, $midClass->getEnumObsolete());
|
$this->assertEquals($midSubClasses, $midClass->getEnumObsolete());
|
||||||
|
|
||||||
// Leaf clasess contain only exactly one node
|
// Leaf clasess contain only exactly one node
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array(DBClassNameTest\ObjectSubSubClass::class => DBClassNameTest\ObjectSubSubClass::class,),
|
[ DBClassNameTest\ObjectSubSubClass::class ],
|
||||||
$leafClass->getEnumObsolete()
|
$leafClass->getEnumObsolete()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,9 @@
|
|||||||
|
|
||||||
namespace SilverStripe\ORM\Tests;
|
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\Core\Config\Config;
|
||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
|
|
||||||
class DataExtensionTest extends SapphireTest
|
class DataExtensionTest extends SapphireTest
|
||||||
@ -207,9 +203,7 @@ class DataExtensionTest extends SapphireTest
|
|||||||
|
|
||||||
public function testExtensionAllMethodNamesHasOwner()
|
public function testExtensionAllMethodNamesHasOwner()
|
||||||
{
|
{
|
||||||
/**
|
/** @var DataExtensionTest\MyObject $do */
|
||||||
* @var DataExtensionTest\MyObject $do
|
|
||||||
*/
|
|
||||||
$do = DataExtensionTest\MyObject::create();
|
$do = DataExtensionTest\MyObject::create();
|
||||||
|
|
||||||
$this->assertTrue($do->hasMethod('getTestValueWith_MyObject'));
|
$this->assertTrue($do->hasMethod('getTestValueWith_MyObject'));
|
||||||
|
@ -12,7 +12,6 @@ use SilverStripe\ORM\Tests\DataObjectSchemaGenerationTest\TestObject;
|
|||||||
|
|
||||||
class DataObjectSchemaGenerationTest extends SapphireTest
|
class DataObjectSchemaGenerationTest extends SapphireTest
|
||||||
{
|
{
|
||||||
|
|
||||||
protected static $extra_dataobjects = array(
|
protected static $extra_dataobjects = array(
|
||||||
TestObject::class,
|
TestObject::class,
|
||||||
TestIndexObject::class
|
TestIndexObject::class
|
||||||
@ -20,6 +19,8 @@ class DataObjectSchemaGenerationTest extends SapphireTest
|
|||||||
|
|
||||||
public static function setUpBeforeClass()
|
public static function setUpBeforeClass()
|
||||||
{
|
{
|
||||||
|
// Start tests
|
||||||
|
static::start();
|
||||||
|
|
||||||
// enable fulltext option on this table
|
// enable fulltext option on this table
|
||||||
TestIndexObject::config()->update(
|
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
|
* Tests the generation of the ClassName spec and ensure it's not unnecessarily influenced
|
||||||
* by the order of classnames of existing records
|
* by the order of classnames of existing records
|
||||||
|
* @skipUpgrade
|
||||||
*/
|
*/
|
||||||
public function testClassNameSpecGeneration()
|
public function testClassNameSpecGeneration()
|
||||||
{
|
{
|
||||||
@ -206,15 +208,12 @@ class DataObjectSchemaGenerationTest extends SapphireTest
|
|||||||
DBClassName::clear_classname_cache();
|
DBClassName::clear_classname_cache();
|
||||||
$do1 = new TestObject();
|
$do1 = new TestObject();
|
||||||
$fields = $schema->databaseFields(TestObject::class, false);
|
$fields = $schema->databaseFields(TestObject::class, false);
|
||||||
/**
|
|
||||||
* @skipUpgrade
|
|
||||||
*/
|
|
||||||
$this->assertEquals("DBClassName", $fields['ClassName']);
|
$this->assertEquals("DBClassName", $fields['ClassName']);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array(
|
[
|
||||||
TestObject::class => TestObject::class,
|
TestObject::class,
|
||||||
TestIndexObject::class => TestIndexObject::class
|
TestIndexObject::class,
|
||||||
),
|
],
|
||||||
$do1->dbObject('ClassName')->getEnum()
|
$do1->dbObject('ClassName')->getEnum()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -224,10 +223,10 @@ class DataObjectSchemaGenerationTest extends SapphireTest
|
|||||||
$item1->write();
|
$item1->write();
|
||||||
DBClassName::clear_classname_cache();
|
DBClassName::clear_classname_cache();
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array(
|
[
|
||||||
TestObject::class => TestObject::class,
|
TestObject::class,
|
||||||
TestIndexObject::class => TestIndexObject::class
|
TestIndexObject::class,
|
||||||
),
|
],
|
||||||
$item1->dbObject('ClassName')->getEnum()
|
$item1->dbObject('ClassName')->getEnum()
|
||||||
);
|
);
|
||||||
$item1->delete();
|
$item1->delete();
|
||||||
@ -237,10 +236,10 @@ class DataObjectSchemaGenerationTest extends SapphireTest
|
|||||||
$item2->write();
|
$item2->write();
|
||||||
DBClassName::clear_classname_cache();
|
DBClassName::clear_classname_cache();
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array(
|
[
|
||||||
TestObject::class => TestObject::class,
|
TestObject::class,
|
||||||
TestIndexObject::class => TestIndexObject::class
|
TestIndexObject::class,
|
||||||
),
|
],
|
||||||
$item2->dbObject('ClassName')->getEnum()
|
$item2->dbObject('ClassName')->getEnum()
|
||||||
);
|
);
|
||||||
$item2->delete();
|
$item2->delete();
|
||||||
@ -252,10 +251,10 @@ class DataObjectSchemaGenerationTest extends SapphireTest
|
|||||||
$item2->write();
|
$item2->write();
|
||||||
DBClassName::clear_classname_cache();
|
DBClassName::clear_classname_cache();
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array(
|
[
|
||||||
TestObject::class => TestObject::class,
|
TestObject::class,
|
||||||
TestIndexObject::class => TestIndexObject::class
|
TestIndexObject::class,
|
||||||
),
|
],
|
||||||
$item1->dbObject('ClassName')->getEnum()
|
$item1->dbObject('ClassName')->getEnum()
|
||||||
);
|
);
|
||||||
$item1->delete();
|
$item1->delete();
|
||||||
|
Loading…
Reference in New Issue
Block a user