Repository: Roave/BetterReflection (php)

Issue:
Create ReflectionFunctions/ReflectionClasses from internal definitions
We currently can't create `ReflectionFunction`s and `ReflectionClass`es from internal functions/class, as the source code is not available. The only way I see is to use the core reflection API, which is a bit of a fudge, and would rely on #36 anyway (would it even be possible to get type hints? maybe not)

_Todo_
- [x] Internal classes (see #80)
- [x] Internal functions


Pull request description:
Allow functions to be deprecated
Related:

* fixes #38 
* https://github.com/phpstan/phpstan-deprecation-rules/issues/24

Patch:
diff --git a/src/Reflection/ReflectionFunctionAbstract.php b/src/Reflection/ReflectionFunctionAbstract.php
index 66b3d6c9..61da484e 100644
--- a/src/Reflection/ReflectionFunctionAbstract.php
+++ b/src/Reflection/ReflectionFunctionAbstract.php
@@ -6,6 +6,7 @@ namespace Roave\BetterReflection\Reflection;
 
 use Closure;
 use LogicException;
+use phpDocumentor\Reflection\DocBlockFactory;
 use phpDocumentor\Reflection\Type;
 use PhpParser\Comment\Doc;
 use PhpParser\Node;
@@ -256,18 +257,18 @@ abstract class ReflectionFunctionAbstract
         return $this->node instanceof Node\Expr\Closure;
     }
 
-    /**
-     * Is this function deprecated?
-     *
-     * Note - we cannot reflect on internal functions (as there is no PHP source
-     * code we can access. This means, at present, we can only EVER return false
-     * from this function.
-     *
-     * @see https://github.com/Roave/BetterReflection/issues/38
-     */
     public function isDeprecated(): bool
     {
-        return false;
+        $docComment = $this->getDocComment();
+
+        if ($docComment === '') {
+            return false;
+        }
+
+        $docBlockFactory = DocBlockFactory::createInstance();
+        $docBlock        = $docBlockFactory->create($docComment);
+
+        return $docBlock->hasTag('deprecated');
     }
 
     public function isInternal(): bool


Test patch:
diff --git a/test/unit/Reflection/ReflectionFunctionAbstractTest.php b/test/unit/Reflection/ReflectionFunctionAbstractTest.php
index 2665b0ad..46a787d1 100644
--- a/test/unit/Reflection/ReflectionFunctionAbstractTest.php
+++ b/test/unit/Reflection/ReflectionFunctionAbstractTest.php
@@ -36,6 +36,7 @@ use function assert;
 use function current;
 use function next;
 use function reset;
+use function sprintf;
 
 /**
  * @covers \Roave\BetterReflection\Reflection\ReflectionFunctionAbstract
@@ -122,9 +123,14 @@ class ReflectionFunctionAbstractTest extends TestCase
         self::assertTrue($function->isClosure());
     }
 
-    public function testIsDeprecated(): void
+    /**
+     * @dataProvider nonDeprecatedProvider
+     */
+    public function testIsDeprecated(string $comment): void
     {
-        $php = '<?php function foo() {}';
+        $php = sprintf('<?php 
+        %s
+        function foo() {}', $comment);
 
         $reflector = new FunctionReflector(new StringSourceLocator($php, $this->astLocator), $this->classReflector);
         $function  = $reflector->reflect('foo');
@@ -132,6 +138,18 @@ class ReflectionFunctionAbstractTest extends TestCase
         self::assertFalse($function->isDeprecated());
     }
 
+    public function nonDeprecatedProvider(): array
+    {
+        return [
+            [''],
+            [
+                '/** 
+                  * @deprecatedPolicy
+                  */',
+            ],
+        ];
+    }
+
     public function testIsInternal(): void
     {
         $php = '<?php function foo() {}';
@@ -711,4 +729,35 @@ PHP;
         self::assertSame(8, $first->getStartLine());
         self::assertSame(10, $first->getEndLine());
     }
+
+    /**
+     * @dataProvider deprecatedDocCommentsProvider
+     */
+    public function testFunctionsCanBeDeprecated(string $comment): void
+    {
+        $php = sprintf('<?php
+        %s
+        function foo() {}', $comment);
+
+        $reflector = new FunctionReflector(new StringSourceLocator($php, $this->astLocator), $this->classReflector);
+        $function  = $reflector->reflect('foo');
+
+        self::assertTrue($function->isDeprecated());
+    }
+
+    public function deprecatedDocCommentsProvider(): array
+    {
+        return [
+            [
+                '/** 
+                  * @deprecated since 7.1
+                  */',
+            ],
+            [
+                '/** 
+                  * @deprecated
+                  */',
+            ],
+        ];
+    }
 }