diff --git a/composer.json b/composer.json index afa689464..2ed3718a7 100644 --- a/composer.json +++ b/composer.json @@ -18,16 +18,17 @@ "require": { "php": ">=5.5.0", "composer/installers": "~1.0", - "monolog/monolog": "~1.11", - "league/flysystem": "~1.0.12", - "symfony/yaml": "~2.7", "embed/embed": "^2.6", + "league/flysystem": "~1.0.12", + "monolog/monolog": "~1.11", + "nikic/php-parser": "^2 || ^3", + "silverstripe/config": "^1@dev", "swiftmailer/swiftmailer": "~5.4", "symfony/cache": "^3.3@dev", "symfony/config": "^2.8", "symfony/translation": "^2.8", - "vlucas/phpdotenv": "^2.4", - "silverstripe/config": "^1@dev" + "symfony/yaml": "~2.7", + "vlucas/phpdotenv": "^2.4" }, "require-dev": { "phpunit/PHPUnit": "~4.8", diff --git a/src/Core/Manifest/ClassContentRemover.php b/src/Core/Manifest/ClassContentRemover.php new file mode 100644 index 000000000..77bbfe035 --- /dev/null +++ b/src/Core/Manifest/ClassContentRemover.php @@ -0,0 +1,86 @@ + T_CLASS, - 1 => array(T_WHITESPACE, 'optional' => true), - 2 => array(T_STRING, 'can_jump_to' => array(7, 14), 'save_to' => 'className'), - 3 => array(T_WHITESPACE, 'optional' => true), - 4 => T_EXTENDS, - 5 => array(T_WHITESPACE, 'optional' => true), - 6 => array(T_STRING, 'save_to' => 'extends[]', 'can_jump_to' => 14), - 7 => array(T_WHITESPACE, 'optional' => true), - 8 => T_IMPLEMENTS, - 9 => array(T_WHITESPACE, 'optional' => true), - 10 => array(T_STRING, 'can_jump_to' => 14, 'save_to' => 'interfaces[]'), - 11 => array(T_WHITESPACE, 'optional' => true), - 12 => array(',', 'can_jump_to' => 10, 'save_to' => 'interfaces[]'), - 13 => array(T_WHITESPACE, 'can_jump_to' => 10), - 14 => array(T_WHITESPACE, 'optional' => true), - 15 => '{', - )); - } - + private $parser; /** - * @return TokenisedRegularExpression + * @var NodeTraverser */ - public static function get_namespaced_class_parser() - { - return new TokenisedRegularExpression(array( - 0 => T_CLASS, - 1 => array(T_WHITESPACE, 'optional' => true), - 2 => array(T_STRING, 'can_jump_to' => array(8, 16), 'save_to' => 'className'), - 3 => array(T_WHITESPACE, 'optional' => true), - 4 => T_EXTENDS, - 5 => array(T_WHITESPACE, 'optional' => true), - 6 => array(T_NS_SEPARATOR, 'save_to' => 'extends[]', 'optional' => true), - 7 => array(T_STRING, 'save_to' => 'extends[]', 'can_jump_to' => array(6, 16)), - 8 => array(T_WHITESPACE, 'optional' => true), - 9 => T_IMPLEMENTS, - 10 => array(T_WHITESPACE, 'optional' => true), - 11 => array(T_NS_SEPARATOR, 'save_to' => 'interfaces[]', 'optional' => true), - 12 => array(T_STRING, 'can_jump_to' => array(11, 16), 'save_to' => 'interfaces[]'), - 13 => array(T_WHITESPACE, 'optional' => true), - 14 => array(',', 'can_jump_to' => 11, 'save_to' => 'interfaces[]'), - 15 => array(T_WHITESPACE, 'can_jump_to' => 11), - 16 => array(T_WHITESPACE, 'optional' => true), - 17 => '{', - )); - } - + private $traverser; /** - * @return TokenisedRegularExpression + * @var ClassManifestVisitor */ - public static function get_trait_parser() - { - return new TokenisedRegularExpression(array( - 0 => T_TRAIT, - 1 => array(T_WHITESPACE, 'optional' => true), - 2 => array(T_STRING, 'save_to' => 'traitName') - )); - } - - /** - * @return TokenisedRegularExpression - */ - public static function get_namespace_parser() - { - return new TokenisedRegularExpression(array( - 0 => T_NAMESPACE, - 1 => array(T_WHITESPACE, 'optional' => true), - 2 => array(T_NS_SEPARATOR, 'save_to' => 'namespaceName[]', 'optional' => true), - 3 => array(T_STRING, 'save_to' => 'namespaceName[]', 'can_jump_to' => 2), - 4 => array(T_WHITESPACE, 'optional' => true), - 5 => ';', - )); - } - - /** - * @return TokenisedRegularExpression - */ - public static function get_interface_parser() - { - return new TokenisedRegularExpression(array( - 0 => T_INTERFACE, - 1 => array(T_WHITESPACE, 'optional' => true), - 2 => array(T_STRING, 'save_to' => 'interfaceName') - )); - } - - /** - * Create a {@link TokenisedRegularExpression} that extracts the namespaces imported with the 'use' keyword - * - * This searches symbols for a `use` followed by 1 or more namespaces which are optionally aliased using the `as` - * keyword. The relevant matching tokens are added one-by-one into an array (using `save_to` param). - * - * eg: use Namespace\ClassName as Alias, OtherNamespace\ClassName; - * - * @return TokenisedRegularExpression - */ - public static function get_imported_namespace_parser() - { - return new TokenisedRegularExpression(array( - 0 => T_USE, - 1 => array(T_WHITESPACE, 'optional' => true), - 2 => array(T_NS_SEPARATOR, 'save_to' => 'importString[]', 'optional' => true), - 3 => array(T_STRING, 'save_to' => 'importString[]', 'can_jump_to' => array(2, 8)), - 4 => array(T_WHITESPACE, 'save_to' => 'importString[]'), - 5 => array(T_AS, 'save_to' => 'importString[]'), - 6 => array(T_WHITESPACE, 'save_to' => 'importString[]'), - 7 => array(T_STRING, 'save_to' => 'importString[]'), - 8 => array(T_WHITESPACE, 'optional' => true), - 9 => array(',', 'save_to' => 'importString[]', 'optional' => true, 'can_jump_to' => 2), - 10 => array(T_WHITESPACE, 'optional' => true, 'can_jump_to' => 2), - 11 => ';', - )); - } + private $visitor; /** * Constructs and initialises a new class manifest, either loading the data @@ -195,6 +93,35 @@ class ClassManifest } } + public function getParser() + { + if (!$this->parser) { + $this->parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); + } + + return $this->parser; + } + + public function getTraverser() + { + if (!$this->traverser) { + $this->traverser = new NodeTraverser; + $this->traverser->addVisitor(new NameResolver); + $this->traverser->addVisitor($this->getVisitor()); + } + + return $this->traverser; + } + + public function getVisitor() + { + if (!$this->visitor) { + $this->visitor = new ClassManifestVisitor; + } + + return $this->visitor; + } + /** * Returns the file path to a class or interface if it exists in the * manifest. @@ -435,125 +362,6 @@ class ClassManifest } } - /** - * Find a the full namespaced declaration of a class (or interface) from a list of candidate imports - * - * This is typically used to determine the full class name in classes that have imported namesapced symbols (having - * used the `use` keyword) - * - * NB: remember the '\\' is an escaped backslash and is interpreted as a single \ - * - * @param string $class The class (or interface) name to find in the candidate imports - * @param string $namespace The namespace that was declared for the classes definition (if there was one) - * @param array $imports The list of imported symbols (Classes or Interfaces) to test against - * - * @return string The fully namespaced class name - */ - protected function findClassOrInterfaceFromCandidateImports($class, $namespace = '', $imports = array()) - { - - //normalise the namespace - $namespace = rtrim($namespace, '\\'); - - //by default we'll use the $class as our candidate - $candidateClass = $class; - - if (!$class) { - return $candidateClass; - } - //if the class starts with a \ then it is explicitly in the global namespace and we don't need to do - // anything else - if (substr($class, 0, 1) == '\\') { - $candidateClass = substr($class, 1); - return $candidateClass; - } - //if there's a namespace, starting assumption is the class is defined in that namespace - if ($namespace) { - $candidateClass = $namespace . '\\' . $class; - } - - if (empty($imports)) { - return $candidateClass; - } - - //normalised class name (PHP is case insensitive for symbols/namespaces - $lClass = strtolower($class); - - //go through all the imports and see if the class exists within one of them - foreach ($imports as $alias => $import) { - //normalise import - $import = trim($import, '\\'); - - //if there is no string key, then there was no declared alias - we'll use the main declaration - if (is_int($alias)) { - $alias = strtolower($import); - } else { - $alias = strtolower($alias); - } - - //exact match? Then it's a class in the global namespace that was imported OR it's an alias of - // another namespace - // or if it ends with the \ClassName then it's the class we are looking for - if ($lClass == $alias - || substr_compare( - $alias, - '\\' . $lClass, - strlen($alias) - strlen($lClass) - 1, - // -1 because the $lClass length is 1 longer due to \ - strlen($alias) - ) === 0 - ) { - $candidateClass = $import; - break; - } - } - return $candidateClass; - } - - /** - * Return an array of array($alias => $import) from tokenizer's tokens of a PHP file - * - * NB: If there is no alias we don't set a key to the array - * - * @param array $tokens The parsed tokens from tokenizer's parsing of a PHP file - * - * @return array The array of imports as (optional) $alias => $import - */ - protected function getImportsFromTokens($tokens) - { - //parse out the imports - $imports = self::get_imported_namespace_parser()->findAll($tokens); - - //if there are any imports, clean them up - // imports come to us as array('importString' => array([array of matching tokens])) - // we need to join this nested array into a string and split out the alias and the import - if (!empty($imports)) { - $cleanImports = array(); - foreach ($imports as $import) { - if (!empty($import['importString'])) { - //join the array up into a string - $importString = implode('', $import['importString']); - //split at , to get each import declaration - $importSet = explode(',', $importString); - foreach ($importSet as $importDeclaration) { - //split at ' as ' (any case) to see if we are aliasing the namespace - $importDeclaration = preg_split('/\s+as\s+/i', $importDeclaration); - //shift off the fully namespaced import - $qualifiedImport = array_shift($importDeclaration); - //if there are still items in the array, it's the alias - if (!empty($importDeclaration)) { - $cleanImports[array_shift($importDeclaration)] = $qualifiedImport; - } else { - $cleanImports[] = $qualifiedImport; - } - } - } - } - $imports = $cleanImports; - } - return $imports; - } - public function handleFile($basename, $pathname, $depth) { if ($basename == self::CONF_FILE) { @@ -563,8 +371,6 @@ class ClassManifest $classes = null; $interfaces = null; - $namespace = null; - $imports = null; $traits = null; // The results of individual file parses are cached, since only a few @@ -577,83 +383,50 @@ class ClassManifest if ($data = $this->cache->load($key)) { $valid = ( isset($data['classes']) && is_array($data['classes']) - && isset($data['interfaces']) && is_array($data['interfaces']) - && isset($data['namespace']) && is_string($data['namespace']) - && isset($data['imports']) && is_array($data['imports']) - && isset($data['traits']) && is_array($data['traits']) + && isset($data['interfaces']) + && is_array($data['interfaces']) + && isset($data['traits']) + && is_array($data['traits']) ); if ($valid) { $classes = $data['classes']; $interfaces = $data['interfaces']; - $namespace = $data['namespace']; - $imports = $data['imports']; $traits = $data['traits']; } } if (!$valid) { - $tokens = token_get_all(file_get_contents($pathname)); - - $classes = self::get_namespaced_class_parser()->findAll($tokens); - $traits = self::get_trait_parser()->findAll($tokens); - - $namespace = self::get_namespace_parser()->findAll($tokens); - - if ($namespace) { - $namespace = implode('', $namespace[0]['namespaceName']); - } else { - $namespace = ''; + $fileContents = ClassContentRemover::remove_class_content($pathname); + try { + $stmts = $this->getParser()->parse($fileContents); + } catch (Error $e) { + // if our mangled contents breaks, try again with the proper file contents + $stmts = $this->getParser()->parse(file_get_contents($pathname)); } + $this->getTraverser()->traverse($stmts); - $imports = $this->getImportsFromTokens($tokens); - - $interfaces = self::get_interface_parser()->findAll($tokens); + $classes = $this->getVisitor()->getClasses(); + $interfaces = $this->getVisitor()->getInterfaces(); + $traits = $this->getVisitor()->getTraits(); $cache = array( 'classes' => $classes, 'interfaces' => $interfaces, - 'namespace' => $namespace, - 'imports' => $imports, - 'traits' => $traits + 'traits' => $traits, ); $this->cache->save($cache, $key); } - // Ensure namespace has no trailing slash, and namespaceBase does - $namespaceBase = ''; - if ($namespace) { - $namespace = rtrim($namespace, '\\'); - $namespaceBase = $namespace . '\\'; - } + foreach ($classes as $className => $classInfo) { + $extends = isset($classInfo['extends']) ? $classInfo['extends'] : null; + $implements = isset($classInfo['interfaces']) ? $classInfo['interfaces'] : null; - foreach ($classes as $class) { - $name = $namespaceBase . $class['className']; - $extends = isset($class['extends']) ? implode('', $class['extends']) : null; - $implements = isset($class['interfaces']) ? $class['interfaces'] : null; - - if ($extends) { - $extends = $this->findClassOrInterfaceFromCandidateImports($extends, $namespace, $imports); - } - - if (!empty($implements)) { - //join all the tokens - $implements = implode('', $implements); - //split at comma - $implements = explode(',', $implements); - //normalise interfaces - foreach ($implements as &$interface) { - $interface = $this->findClassOrInterfaceFromCandidateImports($interface, $namespace, $imports); - } - //release the var name - unset($interface); - } - - $lowercaseName = strtolower($name); + $lowercaseName = strtolower($className); if (array_key_exists($lowercaseName, $this->classes)) { throw new Exception(sprintf( 'There are two files containing the "%s" class: "%s" and "%s"', - $name, + $className, $this->classes[$lowercaseName], $pathname )); @@ -662,15 +435,17 @@ class ClassManifest $this->classes[$lowercaseName] = $pathname; if ($extends) { - $extends = strtolower($extends); + foreach ($extends as $ancestor) { + $ancestor = strtolower($ancestor); - if (!isset($this->children[$extends])) { - $this->children[$extends] = array($name); - } else { - $this->children[$extends][] = $name; + if (!isset($this->children[$ancestor])) { + $this->children[$ancestor] = array($className); + } else { + $this->children[$ancestor][] = $className; + } } } else { - $this->roots[] = $name; + $this->roots[] = $className; } if ($implements) { @@ -678,19 +453,19 @@ class ClassManifest $interface = strtolower($interface); if (!isset($this->implementors[$interface])) { - $this->implementors[$interface] = array($name); + $this->implementors[$interface] = array($className); } else { - $this->implementors[$interface][] = $name; + $this->implementors[$interface][] = $className; } } } } - foreach ($interfaces as $interface) { - $this->interfaces[strtolower($namespaceBase . $interface['interfaceName'])] = $pathname; + foreach ($interfaces as $interfaceName => $interfaceInfo) { + $this->interfaces[strtolower($interfaceName)] = $pathname; } - foreach ($traits as $trait) { - $this->traits[strtolower($namespaceBase . $trait['traitName'])] = $pathname; + foreach ($traits as $traitName => $traitInfo) { + $this->traits[strtolower($traitName)] = $pathname; } } diff --git a/src/Core/Manifest/ClassManifestVisitor.php b/src/Core/Manifest/ClassManifestVisitor.php new file mode 100644 index 000000000..d97307e1c --- /dev/null +++ b/src/Core/Manifest/ClassManifestVisitor.php @@ -0,0 +1,81 @@ +classes = []; + $this->traits = []; + $this->interfaces = []; + } + + public function beforeTraverse(array $nodes) + { + $this->resetState(); + } + + public function enterNode(Node $node) + { + if ($node instanceof Node\Stmt\Class_) { + $extends = ''; + $interfaces = []; + + if ($node->extends) { + $extends = array((string)$node->extends); + } + + if ($node->implements) { + foreach ($node->implements as $interface) { + $interfaces[] = (string)$interface; + } + } + + $this->classes[(string)$node->namespacedName] = [ + 'extends' => $extends, + 'interfaces' => $interfaces, + ]; + } elseif ($node instanceof Node\Stmt\Trait_) { + $this->traits[(string)$node->namespacedName] = array(); + } elseif ($node instanceof Node\Stmt\Interface_) { + $extends = array(); + foreach ($node->extends as $ancestor) { + $extends[] = (string)$ancestor; + } + $this->interfaces[(string)$node->namespacedName] = [ + 'extends' => $extends, + ]; + } + if (!$node instanceof Node\Stmt\Namespace_) { + //break out of traversal as we only need highlevel information here! + return NodeTraverser::DONT_TRAVERSE_CHILDREN; + } + } + + public function getClasses() + { + return $this->classes; + } + + public function getTraits() + { + return $this->traits; + } + + public function getInterfaces() + { + return $this->interfaces; + } +} diff --git a/src/Core/Manifest/TokenisedRegularExpression.php b/src/Core/Manifest/TokenisedRegularExpression.php deleted file mode 100644 index be0909c56..000000000 --- a/src/Core/Manifest/TokenisedRegularExpression.php +++ /dev/null @@ -1,125 +0,0 @@ -expression = $expression; - $this->firstMatch = is_array($expression[0]) ? $expression[0][0] : $expression[0]; - } - - public function findAll($tokens) - { - $tokenTypes = array(); - foreach ($tokens as $i => $token) { - if (is_array($token)) { - $tokenType = $token[0]; - } else { - $tokenType = $token; - // Pre-process string tokens for matchFrom() - $tokens[$i] = array($token, $token); - } - - if ($tokenType == $this->firstMatch) { - $tokenTypes[$i] = $tokenType; - } - } - - $allMatches = array(); - foreach ($tokenTypes as $startKey => $dud) { - $matches = array(); - if ($this->matchFrom($startKey, 0, $tokens, $matches)) { - $allMatches[] = $matches; - } - } - - return $allMatches; - } - - public function matchFrom($tokenPos, $expressionPos, &$tokens, &$matches) - { - $expressionRule = $this->expression[$expressionPos]; - $expectation = is_array($expressionRule) ? $expressionRule[0] : $expressionRule; - if (!is_array($expressionRule)) { - $expressionRule = array(); - } - - if ($expectation == $tokens[$tokenPos][0]) { - if (isset($expressionRule['save_to'])) { - // Append to an array - if (substr($expressionRule['save_to'], -2) == '[]') { - $matches[substr($expressionRule['save_to'], 0, -2)][] = $tokens[$tokenPos][1]; - } // Regular variable setting - else { - $matches[$expressionRule['save_to']] = $tokens[$tokenPos][1]; - } - } - - // End of the expression - if (!isset($this->expression[$expressionPos+1])) { - return true; - - // Process next step as normal - } elseif ($this->matchFrom($tokenPos+1, $expressionPos+1, $tokens, $matches)) { - return true; - - // This step is optional - } elseif (isset($expressionRule['optional']) - && $this->matchFrom($tokenPos, $expressionPos+1, $tokens, $matches)) { - return true; - - // Process jumps - } elseif (isset($expressionRule['can_jump_to'])) { - if (is_array($expressionRule['can_jump_to'])) { - foreach ($expressionRule['can_jump_to'] as $canJumpTo) { - // can_jump_to & optional both set - if (isset($expressionRule['optional']) - && $this->matchFrom($tokenPos, $canJumpTo, $tokens, $matches)) { - return true; - } - // can_jump_to set (optional may or may not be set) - if ($this->matchFrom($tokenPos+1, $canJumpTo, $tokens, $matches)) { - return true; - } - } - } else { - // can_jump_to & optional both set - if (isset($expressionRule['optional']) - && $this->matchFrom($tokenPos, $expressionRule['can_jump_to'], $tokens, $matches)) { - return true; - } - // can_jump_to set (optional may or may not be set) - if ($this->matchFrom($tokenPos+1, $expressionRule['can_jump_to'], $tokens, $matches)) { - return true; - } - } - } - } elseif (isset($expressionRule['optional'])) { - if (isset($this->expression[$expressionPos+1])) { - return $this->matchFrom($tokenPos, $expressionPos+1, $tokens, $matches); - } else { - return true; - } - } elseif (in_array($tokens[$tokenPos][0], array(T_COMMENT, T_DOC_COMMENT, T_WHITESPACE))) { - return $this->matchFrom($tokenPos + 1, $expressionPos, $tokens, $matches); - } - - return false; - } -} diff --git a/tests/php/Core/Manifest/ClassContentRemoverTest.php b/tests/php/Core/Manifest/ClassContentRemoverTest.php new file mode 100644 index 000000000..6870d70eb --- /dev/null +++ b/tests/php/Core/Manifest/ClassContentRemoverTest.php @@ -0,0 +1,52 @@ +assertEquals($expected, $cleanContents); + } + + public function testRemoveClassContentConditional() + { + $filePath = dirname(__FILE__) . '/fixtures/classcontentremover/ContentRemoverTestB.php'; + $cleanContents = ClassContentRemover::remove_class_content($filePath); + + $expected = 'assertEquals($expected, $cleanContents); + } + + public function testRemoveClassContentNoClass() + { + $filePath = dirname(__FILE__) . '/fixtures/classcontentremover/ContentRemoverTestC.php'; + + $cleanContents = ClassContentRemover::remove_class_content($filePath); + + $this->assertEmpty($cleanContents); + } + + public function testRemoveClassContentSillyMethod() + { + $filePath = dirname(__FILE__) . '/fixtures/classcontentremover/ContentRemoverTestD.php'; + + $cleanContents = ClassContentRemover::remove_class_content($filePath); + + $expected = 'assertEquals($expected, $cleanContents); + } +} diff --git a/tests/php/Core/Manifest/NamespacedClassManifestTest.php b/tests/php/Core/Manifest/NamespacedClassManifestTest.php index 5f89e5c83..613893066 100644 --- a/tests/php/Core/Manifest/NamespacedClassManifestTest.php +++ b/tests/php/Core/Manifest/NamespacedClassManifestTest.php @@ -36,57 +36,6 @@ class NamespacedClassManifestTest extends SapphireTest ClassLoader::instance()->popManifest(); } - /** - * @skipUpgrade - */ - public function testGetImportedNamespaceParser() - { - $file = file_get_contents($this->base . DIRECTORY_SEPARATOR . 'module/classes/ClassI.php'); - $tokens = token_get_all($file); - $parsedTokens = ClassManifest::get_imported_namespace_parser()->findAll($tokens); - - $expectedItems = array( - array('SilverStripe', '\\', 'Admin', '\\', 'ModelAdmin'), - array('SilverStripe', '\\', 'Control', '\\', 'Controller', ' ', 'as', ' ', 'Cont'), - array( - 'SilverStripe', '\\', 'Control', '\\', 'HTTPRequest', ' ', 'as', ' ', 'Request', ',', - 'SilverStripe', '\\', 'Control', '\\', 'HTTPResponse', ' ', 'as', ' ', 'Response', ',', - 'SilverStripe', '\\', 'Security', '\\', 'PermissionProvider', ' ', 'as', ' ', 'P', - ), - array('silverstripe', '\\', 'test', '\\', 'ClassA'), - array('\\', 'SilverStripe', '\\', 'Core', '\\', 'Object'), - ); - - $this->assertEquals(count($expectedItems), count($parsedTokens)); - - foreach ($expectedItems as $i => $item) { - $this->assertEquals($item, $parsedTokens[$i]['importString']); - } - } - - public function testGetImportsFromTokens() - { - $file = file_get_contents($this->base . DIRECTORY_SEPARATOR . 'module/classes/ClassI.php'); - $tokens = token_get_all($file); - - $method = new ReflectionMethod($this->manifest, 'getImportsFromTokens'); - $method->setAccessible(true); - - $expectedImports = array( - 'SilverStripe\\Admin\\ModelAdmin', - 'Cont' => 'SilverStripe\\Control\\Controller', - 'Request' => 'SilverStripe\\Control\\HTTPRequest', - 'Response' => 'SilverStripe\\Control\\HTTPResponse', - 'P' => 'SilverStripe\\Security\\PermissionProvider', - 'silverstripe\\test\\ClassA', - '\\SilverStripe\\Core\\Object', - ); - - $imports = $method->invoke($this->manifest, $tokens); - - $this->assertEquals($expectedImports, $imports); - } - public function testClassInfoIsCorrect() { $this->assertContains('SilverStripe\Framework\Tests\ClassI', ClassInfo::implementorsOf('SilverStripe\\Security\\PermissionProvider')); @@ -101,141 +50,6 @@ class NamespacedClassManifestTest extends SapphireTest $this->assertContains('SilverStripe\Framework\Tests\ClassI', ClassInfo::subclassesFor('SilverStripe\\Admin\\ModelAdmin')); } - /** - * @skipUpgrade - */ - public function testFindClassOrInterfaceFromCandidateImports() - { - $method = new ReflectionMethod($this->manifest, 'findClassOrInterfaceFromCandidateImports'); - $method->setAccessible(true); - - $this->assertTrue(ClassInfo::exists('silverstripe\test\ClassA')); - - $this->assertEquals( - 'PermissionProvider', - $method->invokeArgs( - $this->manifest, - [ - '\PermissionProvider', - 'Test\Namespace', - array( - 'TestOnly', - 'Controller', - ), - ] - ) - ); - - $this->assertEquals( - 'PermissionProvider', - $method->invokeArgs( - $this->manifest, - array( - 'PermissionProvider', - 'Test\NAmespace', - array( - 'PermissionProvider', - ) - ) - ) - ); - - $this->assertEmpty( - $method->invokeArgs( - $this->manifest, - array( - '', - 'TextNamespace', - array( - 'PermissionProvider', - ), - ) - ) - ); - - $this->assertEmpty( - $method->invokeArgs( - $this->manifest, - array( - '', - '', - array() - ) - ) - ); - - $this->assertEquals( - 'silverstripe\test\ClassA', - $method->invokeArgs( - $this->manifest, - array( - 'ClassA', - 'Test\Namespace', - array( - 'silverstripe\test\ClassA', - 'PermissionProvider', - ), - ) - ) - ); - - $this->assertEquals( - 'ClassA', - $method->invokeArgs( - $this->manifest, - array( - '\ClassA', - 'Test\Namespace', - array( - 'silverstripe\test', - ), - ) - ) - ); - - $this->assertEquals( - 'ClassA', - $method->invokeArgs( - $this->manifest, - array( - 'ClassA', - 'silverstripe\test', - array( - '\ClassA', - ), - ) - ) - ); - - $this->assertEquals( - 'ClassA', - $method->invokeArgs( - $this->manifest, - array( - 'Alias', - 'silverstripe\test', - array( - 'Alias' => '\ClassA', - ), - ) - ) - ); - - $this->assertEquals( - 'silverstripe\test\ClassA', - $method->invokeArgs( - $this->manifest, - array( - 'ClassA', - 'silverstripe\test', - array( - 'silverstripe\test\ClassB', - ), - ) - ) - ); - } - public function testGetItemPath() { $expect = array( diff --git a/tests/php/Core/Manifest/TokenisedRegularExpressionTest.php b/tests/php/Core/Manifest/TokenisedRegularExpressionTest.php deleted file mode 100644 index 7d66503b2..000000000 --- a/tests/php/Core/Manifest/TokenisedRegularExpressionTest.php +++ /dev/null @@ -1,201 +0,0 @@ -getTokens(); - - $matches = $parser->findAll($tokens); - $classes = array(); - if ($matches) { - foreach ($matches as $match) { - $classes[$match['className']] = $match; - } - } - - $this->assertArrayHasKey('ClassA', $classes); - $this->assertArrayHasKey('ClassB', $classes); - - $this->assertArrayHasKey('ClassC', $classes); - $this->assertEquals(array('ParentClassC'), $classes['ClassC']['extends']); - - $this->assertArrayHasKey('ClassD', $classes); - $this->assertEquals(array('ParentClassD'), $classes['ClassD']['extends']); - $this->assertContains('InterfaceA', $classes['ClassD']['interfaces']); - - $this->assertArrayHasKey('ClassE', $classes); - $this->assertEquals(array('ParentClassE'), $classes['ClassE']['extends']); - $this->assertContains('InterfaceA', $classes['ClassE']['interfaces']); - $this->assertContains('InterfaceB', $classes['ClassE']['interfaces']); - - $this->assertArrayHasKey('ClassF', $classes); - $this->assertEquals(array('ParentClassF'), $classes['ClassF']['extends']); - $this->assertContains('InterfaceA', $classes['ClassF']['interfaces']); - $this->assertContains('InterfaceB', $classes['ClassF']['interfaces']); - } - - public function testNamesapcedClassDefParser() - { - $parser = ClassManifest::get_namespaced_class_parser(); - - $tokens = $this->getNamespaceTokens(); - - $matches = $parser->findAll($tokens); - - $classes = array(); - if ($matches) { - foreach ($matches as $match) { - $classes[$match['className']] = $match; - } - } - - $this->assertArrayHasKey('ClassA', $classes); - $this->assertArrayHasKey('ClassB', $classes); - $this->assertEquals(array('ParentClassB'), $classes['ClassB']['extends']); - - $this->assertArrayHasKey('ClassC', $classes); - $this->assertEquals(array('\\', 'ParentClassC'), $classes['ClassC']['extends']); - - $this->assertArrayHasKey('ClassD', $classes); - $this->assertEquals(array('subtest', '\\', 'ParentClassD'), $classes['ClassD']['extends']); - - $this->assertArrayHasKey('ClassE', $classes); - $this->assertContains('InterfaceE', $classes['ClassE']['interfaces']); - - $this->assertArrayHasKey('ClassF', $classes); - $this->assertEquals(array('\\', 'InterfaceF'), $classes['ClassF']['interfaces']); - } - - public function testInterfaceDefParser() - { - $parser = ClassManifest::get_interface_parser(); - - $tokens = $this->getTokens(); - - $matches = $parser->findAll($tokens); - $interfaces = array(); - if ($matches) { - foreach ($matches as $match) { - $interfaces[$match['interfaceName']] = $match; - } - } - - $this->assertArrayHasKey('InterfaceA', $interfaces); - $this->assertArrayHasKey('InterfaceB', $interfaces); - $this->assertArrayHasKey('InterfaceC', $interfaces); - $this->assertArrayHasKey('InterfaceD', $interfaces); - } - - public function testNamespaceDefParser() - { - $parser = ClassManifest::get_namespace_parser(); - - $namespacedTokens = $this->getNamespaceTokens(); - $tokens = $this->getTokens(); - - $namespacedMatches = $parser->findAll($namespacedTokens); - $matches = $parser->findAll($tokens); - - $this->assertEquals(array(), $matches); - $this->assertEquals(array('silverstripe', '\\', 'test'), $namespacedMatches[0]['namespaceName']); - } -} diff --git a/tests/php/Core/Manifest/fixtures/classcontentremover/ContentRemoverTestA.php b/tests/php/Core/Manifest/fixtures/classcontentremover/ContentRemoverTestA.php new file mode 100644 index 000000000..d7ca94cb6 --- /dev/null +++ b/tests/php/Core/Manifest/fixtures/classcontentremover/ContentRemoverTestA.php @@ -0,0 +1,28 @@ +