[FEATURE] Use symfony/property-info to gather doc block information 54/59454/21
authorAlexander Schnitzler <git@alexanderschnitzler.de>
Wed, 16 Jan 2019 12:21:40 +0000 (13:21 +0100)
committerAnja Leichsenring <aleichsenring@ab-softlab.de>
Sun, 3 Feb 2019 15:26:15 +0000 (16:26 +0100)
This patch introduces the requirement to symfony/property-info
which provides a neat api to extract information about
properties via several different extractors.

The package comes with a PhpDocExtractor, which is kind of
a drop in replacement (functional wise) for the extbase
DocCommentParser which has been removed.

Along with the replacement of the doc block extractor
the package comes with an api to fetch context data that
enables us to resolve non fully qualified class names.

This is now possible:

```
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use ExtbaseTeam\BlogExample\Domain\Model\Comment;

class Post
{
    /*
     * @var ObjectStorage<Comment>
     */
    public $comments;
}
```

Important:
This only works in extbase models as the reflection
costs are high and the information is only needed
in this case.

The non fully qualified class name is now also
supported for injection properties, although it is
still recommended to avoid injection properties in
favor of injection methods or constructor injection.

Example:

```
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;

class Service
{
    /*
     * @Extbase\Inject
     * @var ConfigurationManager
     */
    public $configurationManager;
}
```

Releases: master
Resolves: #87457
Change-Id: I006aeb737b4bb9790fda257e12ba46a105d9b113
Reviewed-on: https://review.typo3.org/59454
Tested-by: TYPO3com <noreply@typo3.com>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Richard Haeser <richard@maxserv.com>
Tested-by: Richard Haeser <richard@maxserv.com>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
14 files changed:
composer.json
composer.lock
typo3/sysext/core/Documentation/Changelog/master/Feature-87457-UseSymfonyproperty-infoToGatherDocBlockInformation.rst [new file with mode: 0644]
typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php
typo3/sysext/extbase/Classes/Reflection/ClassSchema.php
typo3/sysext/extbase/Classes/Reflection/DocBlock/Tags/Null_.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Reflection/DocBlock/Tags/Var_.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Reflection/DocCommentParser.php [deleted file]
typo3/sysext/extbase/Tests/Unit/Reflection/ClassSchema/PropertyTest.php
typo3/sysext/extbase/Tests/Unit/Reflection/ClassSchemaTest.php
typo3/sysext/extbase/Tests/Unit/Reflection/DocCommentParserTest.php [deleted file]
typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithAllTypesOfMethods.php
typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithAllTypesOfProperties.php
typo3/sysext/extbase/composer.json

index e1096ad..086fc39 100644 (file)
@@ -45,6 +45,7 @@
                "guzzlehttp/guzzle": "^6.3.0",
                "mso/idna-convert": "^1.1.0",
                "nikic/php-parser": "^4.0",
+               "phpdocumentor/reflection-docblock": "^4.3",
                "psr/container": "^1.0",
                "psr/http-message": "~1.0",
                "psr/http-server-middleware": "^1.0",
                "symfony/finder": "^4.1",
                "symfony/polyfill-intl-icu": "^1.6",
                "symfony/polyfill-mbstring": "^1.2",
+               "symfony/property-info": "^4.2",
                "symfony/routing": "^4.1",
                "symfony/yaml": "^4.1",
                "typo3/class-alias-loader": "^1.0",
                "typo3/cms-cli": "^2.0",
                "typo3/cms-composer-installers": "^2.0",
                "typo3/phar-stream-wrapper": "^3.0.1",
-               "typo3fluid/fluid": "^2.6.0"
+               "typo3fluid/fluid": "^2.6.0",
+               "webmozart/assert": "^1.0"
        },
        "require-dev": {
                "codeception/codeception": "^2.4.5",
index 0e01c0c..b3c65e4 100644 (file)
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "cd80908ee69343d7f094a589ce995519",
+    "content-hash": "665468d5ffefa8e2e72be0a338838765",
     "packages": [
         {
             "name": "cogpowered/finediff",
             "time": "2018-09-18T07:03:24+00:00"
         },
         {
+            "name": "phpdocumentor/reflection-common",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+                "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
+                "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.6"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": [
+                        "src"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jaap van Otterdijk",
+                    "email": "opensource@ijaap.nl"
+                }
+            ],
+            "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+            "homepage": "http://www.phpdoc.org",
+            "keywords": [
+                "FQSEN",
+                "phpDocumentor",
+                "phpdoc",
+                "reflection",
+                "static analysis"
+            ],
+            "time": "2017-09-11T18:02:19+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-docblock",
+            "version": "4.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+                "reference": "94fd0001232e47129dd3504189fa1c7225010d08"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08",
+                "reference": "94fd0001232e47129dd3504189fa1c7225010d08",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0",
+                "phpdocumentor/reflection-common": "^1.0.0",
+                "phpdocumentor/type-resolver": "^0.4.0",
+                "webmozart/assert": "^1.0"
+            },
+            "require-dev": {
+                "doctrine/instantiator": "~1.0.5",
+                "mockery/mockery": "^1.0",
+                "phpunit/phpunit": "^6.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                }
+            ],
+            "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+            "time": "2017-11-30T07:14:17+00:00"
+        },
+        {
+            "name": "phpdocumentor/type-resolver",
+            "version": "0.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/TypeResolver.git",
+                "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
+                "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5 || ^7.0",
+                "phpdocumentor/reflection-common": "^1.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "^0.9.4",
+                "phpunit/phpunit": "^5.2||^4.8.24"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                }
+            ],
+            "time": "2017-07-14T14:27:02+00:00"
+        },
+        {
             "name": "psr/cache",
             "version": "1.0.1",
             "source": {
             "time": "2018-07-26T11:24:31+00:00"
         },
         {
+            "name": "symfony/inflector",
+            "version": "v4.2.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/inflector.git",
+                "reference": "9f64271222922ef1a10e43f77d88baf72bf22b0e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/inflector/zipball/9f64271222922ef1a10e43f77d88baf72bf22b0e",
+                "reference": "9f64271222922ef1a10e43f77d88baf72bf22b0e",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1.3",
+                "symfony/polyfill-ctype": "~1.8"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.2-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Inflector\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Inflector Component",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "inflection",
+                "pluralize",
+                "singularize",
+                "string",
+                "symfony",
+                "words"
+            ],
+            "time": "2019-01-03T09:07:35+00:00"
+        },
+        {
             "name": "symfony/intl",
             "version": "v4.1.4",
             "source": {
             "time": "2018-08-06T14:22:27+00:00"
         },
         {
+            "name": "symfony/property-info",
+            "version": "v4.2.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/property-info.git",
+                "reference": "cfb42283015c194d14917f4dc108df7efc63fa4a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/property-info/zipball/cfb42283015c194d14917f4dc108df7efc63fa4a",
+                "reference": "cfb42283015c194d14917f4dc108df7efc63fa4a",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1.3",
+                "symfony/inflector": "~3.4|~4.0"
+            },
+            "conflict": {
+                "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0,<3.2.2",
+                "phpdocumentor/type-resolver": "<0.3.0",
+                "symfony/dependency-injection": "<3.4"
+            },
+            "require-dev": {
+                "doctrine/annotations": "~1.0",
+                "phpdocumentor/reflection-docblock": "^3.0|^4.0",
+                "symfony/cache": "~3.4|~4.0",
+                "symfony/dependency-injection": "~3.4|~4.0",
+                "symfony/serializer": "~3.4|~4.0"
+            },
+            "suggest": {
+                "phpdocumentor/reflection-docblock": "To use the PHPDoc",
+                "psr/cache-implementation": "To cache results",
+                "symfony/doctrine-bridge": "To use Doctrine metadata",
+                "symfony/serializer": "To use Serializer metadata"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.2-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\PropertyInfo\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "K√©vin Dunglas",
+                    "email": "dunglas@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Property Info Component",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "doctrine",
+                "phpdoc",
+                "property",
+                "symfony",
+                "type",
+                "validator"
+            ],
+            "time": "2019-01-03T09:07:35+00:00"
+        },
+        {
             "name": "symfony/routing",
             "version": "v4.1.4",
             "source": {
             ],
             "description": "The TYPO3 Fluid template rendering engine",
             "time": "2018-12-07T14:46:13+00:00"
+        },
+        {
+            "name": "webmozart/assert",
+            "version": "1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webmozart/assert.git",
+                "reference": "0df1908962e7a3071564e857d86874dad1ef204a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a",
+                "reference": "0df1908962e7a3071564e857d86874dad1ef204a",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.3 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.6",
+                "sebastian/version": "^1.0.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Webmozart\\Assert\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                }
+            ],
+            "description": "Assertions to validate method input/output with nice error messages.",
+            "keywords": [
+                "assert",
+                "check",
+                "validate"
+            ],
+            "time": "2018-01-29T19:49:41+00:00"
         }
     ],
     "packages-dev": [
             "time": "2018-02-15T16:58:55+00:00"
         },
         {
-            "name": "phpdocumentor/reflection-common",
-            "version": "1.0.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
-                "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
-                "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=5.5"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^4.6"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0.x-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "phpDocumentor\\Reflection\\": [
-                        "src"
-                    ]
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Jaap van Otterdijk",
-                    "email": "opensource@ijaap.nl"
-                }
-            ],
-            "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
-            "homepage": "http://www.phpdoc.org",
-            "keywords": [
-                "FQSEN",
-                "phpDocumentor",
-                "phpdoc",
-                "reflection",
-                "static analysis"
-            ],
-            "time": "2017-09-11T18:02:19+00:00"
-        },
-        {
-            "name": "phpdocumentor/reflection-docblock",
-            "version": "4.3.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
-                "reference": "94fd0001232e47129dd3504189fa1c7225010d08"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08",
-                "reference": "94fd0001232e47129dd3504189fa1c7225010d08",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.0",
-                "phpdocumentor/reflection-common": "^1.0.0",
-                "phpdocumentor/type-resolver": "^0.4.0",
-                "webmozart/assert": "^1.0"
-            },
-            "require-dev": {
-                "doctrine/instantiator": "~1.0.5",
-                "mockery/mockery": "^1.0",
-                "phpunit/phpunit": "^6.4"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.x-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "phpDocumentor\\Reflection\\": [
-                        "src/"
-                    ]
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Mike van Riel",
-                    "email": "me@mikevanriel.com"
-                }
-            ],
-            "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
-            "time": "2017-11-30T07:14:17+00:00"
-        },
-        {
-            "name": "phpdocumentor/type-resolver",
-            "version": "0.4.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/phpDocumentor/TypeResolver.git",
-                "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
-                "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^5.5 || ^7.0",
-                "phpdocumentor/reflection-common": "^1.0"
-            },
-            "require-dev": {
-                "mockery/mockery": "^0.9.4",
-                "phpunit/phpunit": "^5.2||^4.8.24"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0.x-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "phpDocumentor\\Reflection\\": [
-                        "src/"
-                    ]
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Mike van Riel",
-                    "email": "me@mikevanriel.com"
-                }
-            ],
-            "time": "2017-07-14T14:27:02+00:00"
-        },
-        {
             "name": "phpspec/prophecy",
             "version": "1.7.6",
             "source": {
                 "typo3"
             ],
             "time": "2019-01-17T14:51:59+00:00"
-        },
-        {
-            "name": "webmozart/assert",
-            "version": "1.3.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/webmozart/assert.git",
-                "reference": "0df1908962e7a3071564e857d86874dad1ef204a"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a",
-                "reference": "0df1908962e7a3071564e857d86874dad1ef204a",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^5.3.3 || ^7.0"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^4.6",
-                "sebastian/version": "^1.0.1"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.3-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Webmozart\\Assert\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Bernhard Schussek",
-                    "email": "bschussek@gmail.com"
-                }
-            ],
-            "description": "Assertions to validate method input/output with nice error messages.",
-            "keywords": [
-                "assert",
-                "check",
-                "validate"
-            ],
-            "time": "2018-01-29T19:49:41+00:00"
         }
     ],
     "aliases": [],
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-87457-UseSymfonyproperty-infoToGatherDocBlockInformation.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-87457-UseSymfonyproperty-infoToGatherDocBlockInformation.rst
new file mode 100644 (file)
index 0000000..73df469
--- /dev/null
@@ -0,0 +1,56 @@
+.. include:: ../../Includes.txt
+
+===========================================================================
+Feature: #87457 - Use symfony/property-info to gather doc block information
+===========================================================================
+
+See :issue:`87457`
+
+Description
+===========
+
+The use of `symfony/property-info` enables us to resolve non fully qualified class names.
+
+This is now possible:
+
+.. code-block:: php
+
+   use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
+   use ExtbaseTeam\BlogExample\Domain\Model\Comment;
+
+   class Post
+   {
+       /*
+        * @var ObjectStorage<Comment>
+        */
+       public $comments;
+   }
+
+Important:
+This only works in extbase models as the reflection
+costs are high and the information is only needed
+in this case.
+
+The non fully qualified class name is now also
+supported for injection properties, although it is
+still recommended to avoid injection properties in
+favor of injection methods or constructor injection.
+
+Example:
+
+.. code-block:: php
+
+   use TYPO3\CMS\Extbase\Annotation as Extbase;
+   use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
+
+   class Service
+   {
+       /*
+        * @Extbase\Inject
+        * @var ConfigurationManager
+        */
+       public $configurationManager;
+   }
+
+
+.. index:: PHP-API, ext:extbase
index 53b672b..0d92753 100644 (file)
@@ -248,12 +248,14 @@ class DataMapper
             $propertyValue = null;
             if (isset($row[$columnName])) {
                 switch ($propertyType) {
+                    case 'int':
                     case 'integer':
                         $propertyValue = (int)$row[$columnName];
                         break;
                     case 'float':
                         $propertyValue = (double)$row[$columnName];
                         break;
+                    case 'bool':
                     case 'boolean':
                         $propertyValue = (bool)$row[$columnName];
                         break;
index 28612ec..66e3dd8 100644 (file)
@@ -15,6 +15,12 @@ namespace TYPO3\CMS\Extbase\Reflection;
  */
 
 use Doctrine\Common\Annotations\AnnotationReader;
+use phpDocumentor\Reflection\DocBlock\Tags\Param;
+use phpDocumentor\Reflection\DocBlockFactory;
+use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
+use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
+use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
+use Symfony\Component\PropertyInfo\Type;
 use TYPO3\CMS\Core\SingletonInterface;
 use TYPO3\CMS\Core\Utility\ClassNamingUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -32,6 +38,7 @@ use TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchMethodException;
 use TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchPropertyException;
 use TYPO3\CMS\Extbase\Reflection\ClassSchema\Method;
 use TYPO3\CMS\Extbase\Reflection\ClassSchema\Property;
+use TYPO3\CMS\Extbase\Reflection\DocBlock\Tags\Null_;
 use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
 use TYPO3\CMS\Extbase\Validation\Exception\InvalidTypeHintException;
 use TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationConfigurationException;
@@ -107,17 +114,22 @@ class ClassSchema
     /**
      * @var array
      */
-    private $tags;
+    private $injectProperties = [];
 
     /**
      * @var array
      */
-    private $injectProperties = [];
+    private $injectMethods = [];
 
     /**
-     * @var array
+     * @var PropertyInfoExtractor
      */
-    private $injectMethods = [];
+    private static $propertyInfoExtractor;
+
+    /**
+     * @var
+     */
+    private static $docBlockFactory;
 
     /**
      * Constructs this class schema
@@ -148,9 +160,39 @@ class ClassSchema
             $this->modelType = static::MODELTYPE_VALUEOBJECT;
         }
 
-        $docCommentParser = new DocCommentParser(true);
-        $docCommentParser->parseDocComment($reflectionClass->getDocComment());
-        $this->tags = $docCommentParser->getTagsValues();
+        if (self::$propertyInfoExtractor === null) {
+            $docBlockFactory = DocBlockFactory::createInstance();
+            $docBlockFactory->registerTagHandler('var', DocBlock\Tags\Var_::class);
+
+            $phpDocExtractor = new PhpDocExtractor($docBlockFactory);
+            $reflectionExtractor = new ReflectionExtractor();
+
+            self::$propertyInfoExtractor = new PropertyInfoExtractor(
+                [],
+                [$phpDocExtractor, $reflectionExtractor]
+            );
+        }
+
+        if (self::$docBlockFactory === null) {
+            self::$docBlockFactory = DocBlockFactory::createInstance();
+            self::$docBlockFactory->registerTagHandler('author', Null_::class);
+            self::$docBlockFactory->registerTagHandler('covers', Null_::class);
+            self::$docBlockFactory->registerTagHandler('deprecated', Null_::class);
+            self::$docBlockFactory->registerTagHandler('link', Null_::class);
+            self::$docBlockFactory->registerTagHandler('method', Null_::class);
+            self::$docBlockFactory->registerTagHandler('property-read', Null_::class);
+            self::$docBlockFactory->registerTagHandler('property', Null_::class);
+            self::$docBlockFactory->registerTagHandler('property-write', Null_::class);
+            self::$docBlockFactory->registerTagHandler('return', Null_::class);
+            self::$docBlockFactory->registerTagHandler('see', Null_::class);
+            self::$docBlockFactory->registerTagHandler('since', Null_::class);
+            self::$docBlockFactory->registerTagHandler('source', Null_::class);
+            self::$docBlockFactory->registerTagHandler('throw', Null_::class);
+            self::$docBlockFactory->registerTagHandler('throws', Null_::class);
+            self::$docBlockFactory->registerTagHandler('uses', Null_::class);
+            self::$docBlockFactory->registerTagHandler('var', Null_::class);
+            self::$docBlockFactory->registerTagHandler('version', Null_::class);
+        }
 
         $this->reflectProperties($reflectionClass);
         $this->reflectMethods($reflectionClass);
@@ -182,12 +224,6 @@ class ClassSchema
                 'validators'   => []
             ];
 
-            $docCommentParser = new DocCommentParser(true);
-            $docCommentParser->parseDocComment($reflectionProperty->getDocComment());
-            foreach ($docCommentParser->getTagsValues() as $tag => $values) {
-                $this->properties[$propertyName]['tags'][strtolower($tag)] = $values;
-            }
-
             $this->properties[$propertyName]['annotations']['inject'] = false;
             $this->properties[$propertyName]['annotations']['lazy'] = false;
             $this->properties[$propertyName]['annotations']['transient'] = false;
@@ -224,37 +260,51 @@ class ClassSchema
                 $this->properties[$propertyName]['annotations']['transient'] = true;
             }
 
-            if ($propertyName !== 'settings'
-                && ($annotationReader->getPropertyAnnotation($reflectionProperty, Inject::class) instanceof Inject)
+            $isInjectProperty = $propertyName !== 'settings'
+                && ($annotationReader->getPropertyAnnotation($reflectionProperty, Inject::class) instanceof Inject);
+
+            $isPossibleCollectionProperty = $annotationReader->getPropertyAnnotation($reflectionProperty, Transient::class) === null
+                && $this->isModel();
+
+            $types = [];
+            $typesCount = 0;
+            if ($isInjectProperty || $isPossibleCollectionProperty) {
+                /** @var Type[] $types */
+                $types = (array)self::$propertyInfoExtractor->getTypes($this->className, $propertyName);
+                $typesCount = count($types);
+            }
+
+            if ($typesCount > 0
+                && ($annotation = $annotationReader->getPropertyAnnotation($reflectionProperty, Cascade::class)) instanceof Cascade
             ) {
-                try {
-                    $varValue = ltrim($docCommentParser->getTagValues('var')[0], '\\');
-                    $this->properties[$propertyName]['annotations']['inject'] = true;
-                    $this->properties[$propertyName]['annotations']['type'] = $varValue;
-                    $this->properties[$propertyName]['annotations']['dependency'] = $varValue;
-
-                    $this->injectProperties[] = $propertyName;
-                } catch (\Exception $e) {
-                }
+                /** @var Cascade $annotation */
+                $this->properties[$propertyName]['annotations']['cascade'] = $annotation->value;
             }
 
-            if ($docCommentParser->isTaggedWith('var') && $this->properties[$propertyName]['annotations']['transient'] === false) {
-                if (($annotation = $annotationReader->getPropertyAnnotation($reflectionProperty, Cascade::class)) instanceof Cascade) {
-                    /** @var Cascade $annotation */
-                    $this->properties[$propertyName]['annotations']['cascade'] = $annotation->value;
-                }
+            if ($isInjectProperty && ($type = $types[0]) instanceof Type) {
+                $this->properties[$propertyName]['annotations']['inject'] = true;
+                $this->properties[$propertyName]['annotations']['type'] = $type->getClassName();
+                $this->properties[$propertyName]['annotations']['dependency'] = $type->getClassName();
 
-                try {
-                    $type = TypeHandlingUtility::parseType(implode(' ', $docCommentParser->getTagValues('var')));
-                } catch (\Exception $e) {
-                    $type = [
-                        'type' => null,
-                        'elementType' => null
-                    ];
-                }
+                $this->injectProperties[] = $propertyName;
+            }
 
-                $this->properties[$propertyName]['type'] = $type['type'] ? ltrim($type['type'], '\\') : null;
-                $this->properties[$propertyName]['elementType'] = $type['elementType'] ? ltrim($type['elementType'], '\\') : null;
+            if ($isPossibleCollectionProperty) {
+                if ($typesCount === 1) {
+                    $this->properties[$propertyName]['type'] = $types[0]->getClassName() ?? $types[0]->getBuiltinType();
+                } elseif ($typesCount === 2) {
+                    [$type, $elementType] = $types;
+                    $actualType = $type->getClassName() ?? $type->getBuiltinType();
+
+                    if (TypeHandlingUtility::isCollectionType($actualType)
+                        && $elementType->getBuiltinType() === 'array'
+                        && $elementType->getCollectionValueType() instanceof Type
+                        && $elementType->getCollectionValueType()->getClassName() !== null
+                    ) {
+                        $this->properties[$propertyName]['type'] = ltrim($actualType, '\\');
+                        $this->properties[$propertyName]['elementType'] = ltrim($elementType->getCollectionValueType()->getClassName(), '\\');
+                    }
+                }
             }
         }
     }
@@ -280,9 +330,6 @@ class ClassSchema
             $this->methods[$methodName]['annotations']  = [];
             $this->methods[$methodName]['isAction']     = StringUtility::endsWith($methodName, 'Action');
 
-            $docCommentParser = new DocCommentParser(true);
-            $docCommentParser->parseDocComment($reflectionMethod->getDocComment());
-
             $argumentValidators = [];
 
             $annotations = $annotationReader->getMethodAnnotations($reflectionMethod);
@@ -307,19 +354,14 @@ class ClassSchema
                 }
             }
 
-            foreach ($docCommentParser->getTagsValues() as $tag => $values) {
-                $this->methods[$methodName]['tags'][$tag] = array_map(function ($value) {
-                    return ltrim($value, '$');
-                }, $values);
-            }
-
             foreach ($annotations as $annotation) {
                 if ($annotation instanceof IgnoreValidation) {
                     $this->methods[$methodName]['tags']['ignorevalidation'][] = $annotation->argumentName;
                 }
             }
 
-            $this->methods[$methodName]['description'] = $docCommentParser->getDescription();
+            $docComment = $reflectionMethod->getDocComment();
+            $docComment = is_string($docComment) ? $docComment : '';
 
             foreach ($reflectionMethod->getParameters() as $parameterPosition => $reflectionParameter) {
                 /* @var \ReflectionParameter $reflectionParameter */
@@ -356,21 +398,28 @@ class ClassSchema
                 if (($parameterClass = $reflectionParameter->getClass()) instanceof \ReflectionClass) {
                     $this->methods[$methodName]['params'][$parameterName]['class'] = $parameterClass->getName();
                     $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($parameterClass->getName(), '\\');
-                } else {
-                    $methodTagsAndValues = $this->methods[$methodName]['tags'];
-                    if (isset($methodTagsAndValues['param'][$parameterPosition])) {
-                        $explodedParameters = explode(' ', $methodTagsAndValues['param'][$parameterPosition]);
-                        if (count($explodedParameters) >= 2) {
-                            if (TypeHandlingUtility::isSimpleType($explodedParameters[0])) {
-                                // ensure that short names of simple types are resolved correctly to the long form
-                                // this is important for all kinds of type checks later on
-                                $typeInfo = TypeHandlingUtility::parseType($explodedParameters[0]);
-
-                                $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($typeInfo['type'], '\\');
-                            } else {
-                                $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($explodedParameters[0], '\\');
-                            }
-                        }
+                }
+
+                if ($docComment !== '' && $this->methods[$methodName]['params'][$parameterName]['type'] === null) {
+                    /*
+                     * We create (redundant) instances here in this loop due to the fact that
+                     * we do not want to analyse all doc blocks of all available methods. We
+                     * use this technique only if we couldn't grasp all necessary data via
+                     * reflection.
+                     *
+                     * 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.
+                     */
+                    $params = self::$docBlockFactory->create($docComment)
+                        ->getTagsByName('param');
+
+                    if (isset($params[$parameterPosition])) {
+                        /** @var Param $param */
+                        $param = $params[$parameterPosition];
+                        $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($param->getType(), '\\');
                     }
                 }
 
@@ -583,14 +632,6 @@ class ClassSchema
     }
 
     /**
-     * @return array
-     */
-    public function getTags(): array
-    {
-        return $this->tags;
-    }
-
-    /**
      * @return bool
      */
     public function hasInjectProperties(): bool
diff --git a/typo3/sysext/extbase/Classes/Reflection/DocBlock/Tags/Null_.php b/typo3/sysext/extbase/Classes/Reflection/DocBlock/Tags/Null_.php
new file mode 100644 (file)
index 0000000..9a9b747
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+declare(strict_types = 1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Extbase\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
+
+/**
+ * Class TYPO3\CMS\Extbase\Reflection\DocBlock\Tags\Null_
+ */
+class Null_ implements StaticMethod
+{
+    public static function create($body)
+    {
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Reflection/DocBlock/Tags/Var_.php b/typo3/sysext/extbase/Classes/Reflection/DocBlock/Tags/Var_.php
new file mode 100644 (file)
index 0000000..fc4ec12
--- /dev/null
@@ -0,0 +1,100 @@
+<?php
+declare(strict_types = 1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Extbase\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Compound;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Class TYPO3\CMS\Extbase\Reflection\DocBlock\Tags\Var_
+ */
+class Var_ extends \phpDocumentor\Reflection\DocBlock\Tags\Var_
+{
+    /**
+     * @param $body
+     * @param TypeResolver|null $typeResolver
+     * @param DescriptionFactory|null $descriptionFactory
+     * @param TypeContext|null $context
+     * @return \phpDocumentor\Reflection\DocBlock\Tags\Var_|Var_
+     */
+    public static function create(
+        $body,
+        TypeResolver $typeResolver = null,
+        DescriptionFactory $descriptionFactory = null,
+        TypeContext $context = null
+    ) {
+        /*
+         * This class is needed to detect collections like
+         * @var Collection<CollectionType>
+         *
+         * While writing this comment, TYPO3 has a dependency to
+         * phpdocumentor/type-resolver:^0.4.0 via
+         * phpdocumentor/reflection-docblock:4.3.0.
+         *
+         * phpdocumentor/type-resolver can detect collections from
+         * version 0.5.0 on, but as there is no newer version of
+         * phpdocumentor/reflection-docblock, this feature is
+         * unavailable at the moment.
+         *
+         * Once phpdocumentor/reflection-docblock:5.0.0 has been
+         * released, TYPO3 should use that version along with an
+         * updated version of phpdocumentor/type-resolver and
+         * this class should be removed then.
+         */
+        Assert::stringNotEmpty($body);
+        Assert::allNotNull([$typeResolver, $descriptionFactory]);
+
+        $parts        = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
+        $type         = null;
+        $variableName = '';
+
+        // if the first item that is encountered is not a variable; it is a type
+        if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
+            $currentPart = array_shift($parts);
+
+            $matches = [];
+            $pattern = '/(?P<type>[^\s<>]+)<(?P<elementType>[^\s<>]+)>/';
+            if (preg_match($pattern, $currentPart, $matches)) {
+                $type = new Compound([
+                    $typeResolver->resolve($matches['type'], $context),
+                    $typeResolver->resolve($matches['elementType'] . '[]', $context),
+                ]);
+            } else {
+                $type = $typeResolver->resolve($currentPart, $context);
+            }
+
+            array_shift($parts);
+        }
+
+        // if the next item starts with a $ or ...$ it must be the variable name
+        if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) {
+            $variableName = array_shift($parts);
+            array_shift($parts);
+
+            if (substr($variableName, 0, 1) === '$') {
+                $variableName = substr($variableName, 1);
+            }
+        }
+
+        $description = $descriptionFactory->create(implode('', $parts), $context);
+
+        return new static($variableName, $type, $description);
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Reflection/DocCommentParser.php b/typo3/sysext/extbase/Classes/Reflection/DocCommentParser.php
deleted file mode 100644 (file)
index f69d7bc..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-namespace TYPO3\CMS\Extbase\Reflection;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * A little parser which creates tag objects from doc comments
- * @internal only to be used within Extbase, not part of TYPO3 Core API.
- */
-class DocCommentParser
-{
-    /**
-     * @var array
-     */
-    protected static $ignoredTags = ['package', 'subpackage', 'license', 'copyright', 'author', 'version', 'const'];
-
-    /**
-     * @var string The description as found in the doc comment
-     */
-    protected $description = '';
-
-    /**
-     * @var array An array of tag names and their values (multiple values are possible)
-     */
-    protected $tags = [];
-
-    /**
-     * @var bool
-     */
-    private $useIgnoredTags;
-
-    /**
-     * @param bool $useIgnoredTags
-     */
-    public function __construct($useIgnoredTags = false)
-    {
-        $this->useIgnoredTags = $useIgnoredTags;
-    }
-
-    /**
-     * Parses the given doc comment and saves the result (description and
-     * tags) in the parser's object. They can be retrieved by the
-     * getTags() getTagValues() and getDescription() methods.
-     *
-     * @param string $docComment A doc comment as returned by the reflection getDocComment() method
-     */
-    public function parseDocComment($docComment)
-    {
-        $this->description = '';
-        $this->tags = [];
-        $lines = explode(LF, $docComment);
-        foreach ($lines as $line) {
-            if ($line !== '' && strpos($line, '@') !== false) {
-                $this->parseTag(substr($line, strpos($line, '@')));
-            } elseif (empty($this->tags)) {
-                $this->description .= preg_replace('#\\s*/?[*/]*(.*)$#', '$1', $line) . LF;
-            }
-        }
-        $this->description = trim($this->description);
-    }
-
-    /**
-     * Returns the tags which have been previously parsed
-     *
-     * @return array Array of tag names and their (multiple) values
-     */
-    public function getTagsValues()
-    {
-        return $this->tags;
-    }
-
-    /**
-     * Returns the values of the specified tag. The doc comment
-     * must be parsed with parseDocComment() before tags are
-     * available.
-     *
-     * @param string $tagName The tag name to retrieve the values for
-     * @throws \RuntimeException
-     * @return array The tag's values
-     */
-    public function getTagValues($tagName)
-    {
-        if (!$this->isTaggedWith($tagName)) {
-            throw new \RuntimeException('Tag "' . $tagName . '" does not exist.', 1169128255);
-        }
-        return $this->tags[$tagName];
-    }
-
-    /**
-     * Checks if a tag with the given name exists
-     *
-     * @param string $tagName The tag name to check for
-     * @return bool TRUE the tag exists, otherwise FALSE
-     */
-    public function isTaggedWith($tagName)
-    {
-        return isset($this->tags[$tagName]);
-    }
-
-    /**
-     * Returns the description which has been previously parsed
-     *
-     * @return string The description which has been parsed
-     */
-    public function getDescription()
-    {
-        return $this->description;
-    }
-
-    /**
-     * Parses a line of a doc comment for a tag and its value.
-     * The result is stored in the interal tags array.
-     *
-     * @param string $line A line of a doc comment which starts with an @-sign
-     */
-    protected function parseTag($line)
-    {
-        $tagAndValue = preg_split('/\\s/', $line, 2);
-        $tag = substr($tagAndValue[0], 1);
-
-        if ($this->useIgnoredTags && in_array($tag, static::$ignoredTags, true)) {
-            return;
-        }
-
-        if (count($tagAndValue) > 1) {
-            $this->tags[$tag][] = trim($tagAndValue[1]);
-        } else {
-            $this->tags[$tag] = [];
-        }
-    }
-}
index 7e84e16..8eb0cc3 100644 (file)
@@ -126,6 +126,18 @@ class PropertyTest extends UnitTestCase
     /**
      * @test
      */
+    public function classSchemaDetectsTypeAndElementTypeWithoutFQCN(): void
+    {
+        $property = (new ClassSchema(DummyClassWithAllTypesOfProperties::class))
+            ->getProperty('propertyWithObjectStorageAnnotationWithoutFQCN');
+
+        static::assertSame(ObjectStorage::class, $property->getType());
+        static::assertSame(DummyClassWithAllTypesOfProperties::class, $property->getElementType());
+    }
+
+    /**
+     * @test
+     */
     public function classSchemaDetectsValidateAnnotationsModelProperties(): void
     {
         $this->resetSingletonInstances = true;
index 9b9224e..de1a3c3 100644 (file)
@@ -64,7 +64,8 @@ class ClassSchemaTest extends UnitTestCase
                 'propertyWithTransientAnnotation',
                 'propertyWithCascadeAnnotation',
                 'propertyWithCascadeAnnotationWithoutVarAnnotation',
-                'propertyWithObjectStorageAnnotation'
+                'propertyWithObjectStorageAnnotation',
+                'propertyWithObjectStorageAnnotationWithoutFQCN',
             ],
             array_keys((new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class))->getProperties())
         );
@@ -99,7 +100,8 @@ class ClassSchemaTest extends UnitTestCase
                 'methodWithMandatoryParam',
                 'methodWithNullableParam',
                 'methodWithDefaultValueParam',
-                'methodWithTypeHintedParam'
+                'methodWithTypeHintedParam',
+                'methodWithDocBlockTypeHintOnly',
             ],
             array_keys((new ClassSchema(Fixture\DummyClassWithAllTypesOfMethods::class))->getMethods())
         );
@@ -118,13 +120,13 @@ class ClassSchemaTest extends UnitTestCase
         static::assertSame(Fixture\DummyClassWithInjectDoctrineAnnotation::class, $injectProperties['propertyWithFullQualifiedClassName']);
 
         static::assertArrayHasKey('propertyWithRelativeClassName', $injectProperties);
-        static::assertSame('DummyClassWithInjectDoctrineAnnotation', $injectProperties['propertyWithRelativeClassName']);
+        static::assertSame(Fixture\DummyClassWithInjectDoctrineAnnotation::class, $injectProperties['propertyWithRelativeClassName']);
 
         static::assertArrayHasKey('propertyWithImportedClassName', $injectProperties);
-        static::assertSame('ClassSchemaTest', $injectProperties['propertyWithImportedClassName']);
+        static::assertSame(self::class, $injectProperties['propertyWithImportedClassName']);
 
         static::assertArrayHasKey('propertyWithImportedAndAliasedClassName', $injectProperties);
-        static::assertSame('AliasedClassSchemaTest', $injectProperties['propertyWithImportedAndAliasedClassName']);
+        static::assertSame(self::class, $injectProperties['propertyWithImportedAndAliasedClassName']);
     }
 
     /**
@@ -203,23 +205,6 @@ class ClassSchemaTest extends UnitTestCase
     /**
      * @test
      */
-    public function testClassSchemaGetTags()
-    {
-        $tags = (new ClassSchema(Fixture\DummyClassWithTags::class))->getTags();
-        static::assertArrayHasKey('see', $tags);
-
-        // test ignored tags
-        static::assertArrayNotHasKey('package', $tags);
-        static::assertArrayNotHasKey('subpackage', $tags);
-        static::assertArrayNotHasKey('license', $tags);
-        static::assertArrayNotHasKey('copyright', $tags);
-        static::assertArrayNotHasKey('author', $tags);
-        static::assertArrayNotHasKey('version', $tags);
-    }
-
-    /**
-     * @test
-     */
     public function classSchemaGenerationThrowsExceptionWithValidateDoctrineAnnotationsForParamWithoutTypeHint()
     {
         $this->resetSingletonInstances = true;
@@ -242,4 +227,51 @@ class ClassSchemaTest extends UnitTestCase
 
         new ClassSchema(Fixture\DummyControllerWithValidateAnnotationWithoutParam::class);
     }
+
+    /**
+     * @test
+     */
+    public function classSchemaDetectsMethodParameterTypeViaReflection(): void
+    {
+        $class = new class {
+            public function foo(string $foo)
+            {
+            }
+
+            public function bar(ClassSchema $foo)
+            {
+            }
+        };
+
+        $classSchema = new ClassSchema(get_class($class));
+        static::assertSame('string', $classSchema->getMethod('foo')->getParameter('foo')->getType());
+        static::assertSame(ClassSchema::class, $classSchema->getMethod('bar')->getParameter('foo')->getType());
+    }
+
+    /**
+     * @test
+     */
+    public function classSchemaPrefersMethodParameterTypeDetectionViaReflection(): void
+    {
+        $class = new class {
+            /**
+             * @param ClassSchema $foo
+             */
+            public function foo(string $foo)
+            {
+            }
+        };
+
+        $classSchema = new ClassSchema(get_class($class));
+        static::assertSame('string', $classSchema->getMethod('foo')->getParameter('foo')->getType());
+    }
+
+    /**
+     * @test
+     */
+    public function classSchemaDetectsMethodParameterTypeDetectionViaDocBlocksIfNoTypeHintIsGiven(): void
+    {
+        $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfMethods::class);
+        static::assertSame(Fixture\DummyClassWithAllTypesOfMethods::class, $classSchema->getMethod('methodWithDocBlockTypeHintOnly')->getParameter('param')->getType());
+    }
 }
diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/DocCommentParserTest.php b/typo3/sysext/extbase/Tests/Unit/Reflection/DocCommentParserTest.php
deleted file mode 100644 (file)
index b4404a9..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-namespace TYPO3\CMS\Extbase\Tests\Unit\Reflection;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use TYPO3\CMS\Extbase\Reflection\DocCommentParser;
-use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
-
-/**
- * Test case
- */
-class DocCommentParserTest extends UnitTestCase
-{
-    /**
-     * @test
-     */
-    public function stripsSlashFromMethodComment()
-    {
-        $commentParser = new DocCommentParser();
-        $comment = '/**
- * Here is some text, but neither an argument nor a return type.
- */
-';
-        $commentParser->parseDocComment($comment);
-        $this->assertSame(
-            'Here is some text, but neither an argument nor a return type.',
-            $commentParser->getDescription()
-        );
-    }
-}
index 02846b6..4670d11 100644 (file)
@@ -80,4 +80,11 @@ class DummyClassWithAllTypesOfMethods
     public static function methodWithTypeHintedParam(string $param)
     {
     }
+
+    /**
+     * @param \TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture\DummyClassWithAllTypesOfMethods $param
+     */
+    public function methodWithDocBlockTypeHintOnly($param)
+    {
+    }
 }
index 2694eea..bfa9128 100644 (file)
@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture;
 
 use TYPO3\CMS\Extbase\Annotation as Extbase;
 use TYPO3\CMS\Extbase\Annotation\ORM\Transient;
+use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
 
 /**
  * Fixture class with getters and setters
@@ -70,4 +71,9 @@ class DummyClassWithAllTypesOfProperties
      * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture\DummyClassWithAllTypesOfProperties>
      */
     public $propertyWithObjectStorageAnnotation;
+
+    /**
+     * @var ObjectStorage<DummyClassWithAllTypesOfProperties>
+     */
+    public $propertyWithObjectStorageAnnotationWithoutFQCN;
 }
index 223f1fb..c00971e 100644 (file)
                "sort-packages": true
        },
        "require": {
-               "typo3/cms-core": "10.0.*@dev"
+               "phpdocumentor/reflection-docblock": "^4.3",
+               "symfony/property-info": "^4.2",
+               "typo3/cms-core": "10.0.*@dev",
+               "webmozart/assert": "^1.0"
        },
        "suggest": {
                "typo3/cms-scheduler": "Additional scheduler tasks"