[TASK] Deprecate parameter type resolving via doc blocks
authorAlexander Schnitzler <git@alexanderschnitzler.de>
Tue, 15 Jan 2019 17:07:56 +0000 (18:07 +0100)
committerBenni Mack <benni@typo3.org>
Mon, 14 Jun 2021 18:12:52 +0000 (20:12 +0200)
When a ClassSchema is created for a class, all methods inside said
class are analyzed along with their parameters. Ever since extbase
existed, the parameter type has been read from the doc block.

```
/**
 * @param \Foo\Bar\Baz $baz
 */
public function injectBaz($baz);
```

Since the whole reflection api and PHP itself have been improved
in the recent past, parameter types can be detected without looking
at doc blocks. It is recommended to use native type hints as follows:

```
public function injectBaz(\Foo\Bar\Baz $baz);
```

The doc block is neiter needed nor supported in the near future.
Of course, developers may add a doc block but TYPO3 will no longer
evaluate that as of version 12.0.

Releases: master
Resolves: #94115
Change-Id: Ifc68c0cb8a42b8ab589043afbaebf5dd5763eee5
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/59450
Reviewed-by: Oliver Bartsch <bo@cedev.de>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Oliver Bartsch <bo@cedev.de>
Tested-by: core-ci <typo3@b13.com>
Tested-by: Benni Mack <benni@typo3.org>
typo3/sysext/core/Documentation/Changelog/master/Deprecation-94115-ParameterTypeEvaluationViaDocBlockComments.rst [new file with mode: 0644]
typo3/sysext/extbase/Classes/Reflection/ClassSchema.php
typo3/sysext/extbase/Tests/Unit/Reflection/ClassSchemaTest.php
typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyController.php

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-94115-ParameterTypeEvaluationViaDocBlockComments.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-94115-ParameterTypeEvaluationViaDocBlockComments.rst
new file mode 100644 (file)
index 0000000..f69ea1b
--- /dev/null
@@ -0,0 +1,53 @@
+.. include:: ../../Includes.txt
+
+=====================================================================
+Deprecation: #94115 - Parameter Type Evaluation via DocBlock comments
+=====================================================================
+
+See :issue:`94115`
+
+Description
+===========
+
+Extbase had a long support for detecting the actual
+target type of a method argument by parsing the DocBlock
+annotations like
+
+.. code-block:: php
+
+   /**
+    * @param \MyVendor\MyExtension\MyModel $item
+    */
+   public function myAction($item);
+
+However, since PHP 7 supports to define the target type
+by specifying the type directly in the language, which is
+much faster, the "legacy" way of handling type detection for
+arguments are marked as deprecated.
+
+
+Impact
+======
+
+When a DocBlock annotation like :php:`@param \MyClass $item` is added, but
+the actual type is not added to the method argument via native PHP type
+declarations, a deprecation message is now triggered.
+
+
+Affected Installations
+======================
+
+TYPO3 installations with custom Extbase extensions which
+were never upgraded to support latest PHP language constructs.
+
+
+Migration
+=========
+
+Use native PHP type declarations instead - this can be achieved since TYPO3 v10:
+
+.. code-block:: php
+
+   public function myAction(\MyVendor\MyExtension\MyModel $item);
+
+.. index:: PHP-API, NotScanned, ext:extbase
index 7fd4e28..6232708 100644 (file)
@@ -406,6 +406,7 @@ class ClassSchema
                     }
                 }
 
+                $typeDetectedViaDocBlock = false;
                 if ($docComment !== '' && $this->methods[$methodName]['params'][$parameterName]['type'] === null) {
                     /*
                      * We create (redundant) instances here in this loop due to the fact that
@@ -416,8 +417,7 @@ class ClassSchema
                      * Also, if we analyze all method doc blocks, we will trigger numerous errors
                      * due to non PSR-5 compatible tags in the core and in user land code.
                      *
-                     * Fetching the data type via doc blocks will also be deprecated and removed
-                     * in the near future.
+                     * Fetching the data type via doc blocks is deprecated and will be removed in the near future.
                      */
                     $params = self::$docBlockFactory->create($docComment)
                         ->getTagsByName('param');
@@ -426,6 +426,7 @@ class ClassSchema
                         /** @var Param $param */
                         $param = $params[$parameterPosition];
                         $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim((string)$param->getType(), '\\');
+                        $typeDetectedViaDocBlock = true;
                     }
                 }
 
@@ -433,6 +434,14 @@ class ClassSchema
                 if ($reflectionType instanceof \ReflectionNamedType && !$reflectionType->isBuiltin()
                     && ($reflectionMethod->isConstructor() || $this->hasInjectMethodName($reflectionMethod))
                 ) {
+                    if ($typeDetectedViaDocBlock) {
+                        $parameterType = $this->methods[$methodName]['params'][$parameterName]['type'];
+                        $errorMessage = <<<MESSAGE
+The type ($parameterType) of parameter \$$parameterName of method $this->className::$methodName() is defined via php DocBlock, which is deprecated and will be removed in TYPO3 12.0. Use a proper parameter type hint instead:
+[private|protected|public] function $methodName($parameterType \$$parameterName)
+MESSAGE;
+                        trigger_error($errorMessage, E_USER_DEPRECATED);
+                    }
                     $this->methods[$methodName]['params'][$parameterName]['dependency'] = $reflectionType->getName();
                 }
 
@@ -440,10 +449,19 @@ class ClassSchema
                 if (isset($argumentValidators[$parameterName])) {
                     if ($this->methods[$methodName]['params'][$parameterName]['type'] === null) {
                         throw new InvalidTypeHintException(
-                            'Missing type information for parameter "$' . $parameterName . '" in ' . $this->className . '->' . $methodName . '(): Either use an @param annotation or use a type hint.',
+                            'Missing type information for parameter "$' . $parameterName . '" in ' . $this->className . '->' . $methodName . '(): Use a type hint.',
                             1515075192
                         );
                     }
+                    if ($typeDetectedViaDocBlock) {
+                        $parameterType = $this->methods[$methodName]['params'][$parameterName]['type'];
+                        $errorMessage = <<<MESSAGE
+The type ($parameterType) of parameter \$$parameterName of method $this->className::$methodName() is defined via php DocBlock, which is deprecated and will be removed in TYPO3 12.0. Use a proper parameter type hint instead:
+[private|protected|public] function $methodName($parameterType \$$parameterName)
+MESSAGE;
+
+                        trigger_error($errorMessage, E_USER_DEPRECATED);
+                    }
 
                     $this->methods[$methodName]['params'][$parameterName]['validators'] = $argumentValidators[$parameterName];
                     unset($argumentValidators[$parameterName]);
index 86d071c..50dbff9 100644 (file)
@@ -217,7 +217,7 @@ class ClassSchemaTest extends UnitTestCase
     {
         $this->resetSingletonInstances = true;
         $this->expectException(InvalidTypeHintException::class);
-        $this->expectExceptionMessage('Missing type information for parameter "$fooParam" in TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture\DummyControllerWithValidateAnnotationWithoutParamTypeHint->methodWithValidateAnnotationsAction(): Either use an @param annotation or use a type hint.');
+        $this->expectExceptionMessage('Missing type information for parameter "$fooParam" in TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture\DummyControllerWithValidateAnnotationWithoutParamTypeHint->methodWithValidateAnnotationsAction(): Use a type hint.');
         $this->expectExceptionCode(1515075192);
 
         new ClassSchema(DummyControllerWithValidateAnnotationWithoutParamTypeHint::class);
index 2ff2e31..e8dddf4 100644 (file)
@@ -33,7 +33,6 @@ class DummyController extends ActionController
     }
 
     /**
-     * @param string $fooParam
      * @Extbase\Validate(param="fooParam", validator="StringLength", options={"minimum": 1, "maximum": 10})
      * @Extbase\Validate(param="fooParam", validator="NotEmpty")
      * @Extbase\Validate(param="fooParam", validator="TYPO3.CMS.Extbase:NotEmpty")
@@ -41,7 +40,7 @@ class DummyController extends ActionController
      * @Extbase\Validate(param="fooParam", validator="\TYPO3\CMS\Extbase\Validation\Validator\NotEmptyValidator")
      * @Extbase\Validate(param="fooParam", validator="TYPO3\CMS\Extbase\Validation\Validator\NotEmptyValidator")
      */
-    public function methodWithValidateAnnotationsAction($fooParam): void
+    public function methodWithValidateAnnotationsAction(string $fooParam): void
     {
     }
 }