diff --git a/.travis.yml b/.travis.yml index 555f041aa..e75c39eff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,16 @@ branches: - /^feature\/.*$/ - /^optimization\/.*$/ -before_script: travis_retry composer install --no-interaction +matrix: + fast_finish: true + include: + - php: '7.0' + env: PHPDOCUMENTOR_REFLECTION_DOCBLOCK="^2.0" + +before_script: + - if [ -n "$PHPDOCUMENTOR_REFLECTION_DOCBLOCK" ]; then + composer require "phpdocumentor/reflection-docblock:${PHPDOCUMENTOR_REFLECTION_DOCBLOCK}" --no-update; + fi; + - travis_retry composer update --no-interaction script: vendor/bin/phpspec run -fpretty -v diff --git a/composer.json b/composer.json index f190867e6..23131b201 100644 --- a/composer.json +++ b/composer.json @@ -16,16 +16,17 @@ "email": "marcello.duarte@gmail.com" } ], + "require": { "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "~2.0", - "sebastian/comparator": "~1.1", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "sebastian/comparator": "^1.1", "doctrine/instantiator": "^1.0.2", - "sebastian/recursion-context": "~1.0" + "sebastian/recursion-context": "^1.0" }, "require-dev": { - "phpspec/phpspec": "~2.0" + "phpspec/phpspec": "^2.0" }, "autoload": { diff --git a/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.php b/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.php index e33d2ed43..23891c098 100644 --- a/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.php +++ b/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.php @@ -11,17 +11,27 @@ namespace Prophecy\Doubler\ClassPatch; -use phpDocumentor\Reflection\DocBlock; use Prophecy\Doubler\Generator\Node\ClassNode; use Prophecy\Doubler\Generator\Node\MethodNode; +use Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever; +use Prophecy\PhpDocumentor\MethodTagRetrieverInterface; /** * Discover Magical API using "@method" PHPDoc format. * * @author Thomas Tourlourat + * @author Kévin Dunglas + * @author Théo FIDRY */ class MagicCallPatch implements ClassPatchInterface { + private $tagRetriever; + + public function __construct(MethodTagRetrieverInterface $tagRetriever = null) + { + $this->tagRetriever = null === $tagRetriever ? new ClassAndInterfaceTagRetriever() : $tagRetriever; + } + /** * Support any class * @@ -44,15 +54,7 @@ public function apply(ClassNode $node) $parentClass = $node->getParentClass(); $reflectionClass = new \ReflectionClass($parentClass); - $phpdoc = new DocBlock($reflectionClass->getDocComment()); - - $tagList = $phpdoc->getTagsByName('method'); - - $interfaces = $reflectionClass->getInterfaces(); - foreach($interfaces as $interface) { - $phpdoc = new DocBlock($interface); - $tagList = array_merge($tagList, $phpdoc->getTagsByName('method')); - } + $tagList = $this->tagRetriever->getTagList($reflectionClass); foreach($tagList as $tag) { $methodName = $tag->getMethodName(); @@ -62,7 +64,7 @@ public function apply(ClassNode $node) } if (!$reflectionClass->hasMethod($methodName)) { - $methodNode = new MethodNode($tag->getMethodName()); + $methodNode = new MethodNode($methodName); $methodNode->setStatic($tag->isStatic()); $node->addMethod($methodNode); diff --git a/src/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php b/src/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php new file mode 100644 index 000000000..209821ce9 --- /dev/null +++ b/src/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php @@ -0,0 +1,69 @@ + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\PhpDocumentor; + +use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; +use phpDocumentor\Reflection\DocBlock\Tags\Method; + +/** + * @author Théo FIDRY + * + * @internal + */ +final class ClassAndInterfaceTagRetriever implements MethodTagRetrieverInterface +{ + private $classRetriever; + + public function __construct(MethodTagRetrieverInterface $classRetriever = null) + { + if (null !== $classRetriever) { + $this->classRetriever = $classRetriever; + + return; + } + + $this->classRetriever = class_exists('phpDocumentor\Reflection\DocBlockFactory') && class_exists('phpDocumentor\Reflection\Types\ContextFactory') + ? new ClassTagRetriever() + : new LegacyClassTagRetriever() + ; + } + + /** + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[]|Method[] + */ + public function getTagList(\ReflectionClass $reflectionClass) + { + return array_merge( + $this->classRetriever->getTagList($reflectionClass), + $this->getInterfacesTagList($reflectionClass) + ); + } + + /** + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[]|Method[] + */ + private function getInterfacesTagList(\ReflectionClass $reflectionClass) + { + $interfaces = $reflectionClass->getInterfaces(); + $tagList = array(); + + foreach($interfaces as $interface) { + $tagList = array_merge($tagList, $this->classRetriever->getTagList($interface)); + } + + return $tagList; + } +} diff --git a/src/Prophecy/PhpDocumentor/ClassTagRetriever.php b/src/Prophecy/PhpDocumentor/ClassTagRetriever.php new file mode 100644 index 000000000..1d2da8f03 --- /dev/null +++ b/src/Prophecy/PhpDocumentor/ClassTagRetriever.php @@ -0,0 +1,52 @@ + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\PhpDocumentor; + +use phpDocumentor\Reflection\DocBlock\Tags\Method; +use phpDocumentor\Reflection\DocBlockFactory; +use phpDocumentor\Reflection\Types\ContextFactory; + +/** + * @author Théo FIDRY + * + * @internal + */ +final class ClassTagRetriever implements MethodTagRetrieverInterface +{ + private $docBlockFactory; + private $contextFactory; + + public function __construct() + { + $this->docBlockFactory = DocBlockFactory::createInstance(); + $this->contextFactory = new ContextFactory(); + } + + /** + * @param \ReflectionClass $reflectionClass + * + * @return Method[] + */ + public function getTagList(\ReflectionClass $reflectionClass) + { + try { + $phpdoc = $this->docBlockFactory->create( + $reflectionClass, + $this->contextFactory->createFromReflector($reflectionClass) + ); + + return $phpdoc->getTagsByName('method'); + } catch (\InvalidArgumentException $e) { + return array(); + } + } +} diff --git a/src/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php b/src/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php new file mode 100644 index 000000000..c0dec3de8 --- /dev/null +++ b/src/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php @@ -0,0 +1,35 @@ + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\PhpDocumentor; + +use phpDocumentor\Reflection\DocBlock; +use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; + +/** + * @author Théo FIDRY + * + * @internal + */ +final class LegacyClassTagRetriever implements MethodTagRetrieverInterface +{ + /** + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[] + */ + public function getTagList(\ReflectionClass $reflectionClass) + { + $phpdoc = new DocBlock($reflectionClass->getDocComment()); + + return $phpdoc->getTagsByName('method'); + } +} diff --git a/src/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php b/src/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php new file mode 100644 index 000000000..d3989dad5 --- /dev/null +++ b/src/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php @@ -0,0 +1,30 @@ + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\PhpDocumentor; + +use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; +use phpDocumentor\Reflection\DocBlock\Tags\Method; + +/** + * @author Théo FIDRY + * + * @internal + */ +interface MethodTagRetrieverInterface +{ + /** + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[]|Method[] + */ + public function getTagList(\ReflectionClass $reflectionClass); +}