王新凯 4 years ago
parent
commit
bd37410fed
100 changed files with 10467 additions and 3 deletions
  1. 2 1
      composer.json
  2. 62 0
      config/apidoc.php
  3. BIN
      public/apidoc/favicon.ico
  4. 1 0
      public/apidoc/index.html
  5. BIN
      public/apidoc/logo.png
  6. 0 0
      public/apidoc/static/css/app.d65fcd89.css
  7. 0 0
      public/apidoc/static/css/chunk-vendors.39c71c89.css
  8. 0 0
      public/apidoc/static/js/app.9ecb1b87.js
  9. 0 0
      public/apidoc/static/js/chunk-vendors.52db4364.js
  10. 4 0
      vendor/composer/autoload_psr4.php
  11. 26 0
      vendor/composer/autoload_static.php
  12. 287 0
      vendor/composer/installed.json
  13. 162 0
      vendor/doctrine/annotations/CHANGELOG.md
  14. 19 0
      vendor/doctrine/annotations/LICENSE
  15. 22 0
      vendor/doctrine/annotations/README.md
  16. 42 0
      vendor/doctrine/annotations/composer.json
  17. 271 0
      vendor/doctrine/annotations/docs/en/annotations.rst
  18. 443 0
      vendor/doctrine/annotations/docs/en/custom.rst
  19. 101 0
      vendor/doctrine/annotations/docs/en/index.rst
  20. 6 0
      vendor/doctrine/annotations/docs/en/sidebar.rst
  21. 59 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php
  22. 21 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php
  23. 15 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php
  24. 69 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php
  25. 43 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php
  26. 13 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/NamedArgumentConstructor.php
  27. 13 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php
  28. 101 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php
  29. 171 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php
  30. 389 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php
  31. 190 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php
  32. 264 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php
  33. 129 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php
  34. 1459 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php
  35. 315 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php
  36. 171 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.php
  37. 100 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php
  38. 14 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/NamedArgumentConstructorAnnotation.php
  39. 92 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php
  40. 80 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php
  41. 114 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php
  42. 208 0
      vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php
  43. 19 0
      vendor/doctrine/lexer/LICENSE
  44. 9 0
      vendor/doctrine/lexer/README.md
  45. 41 0
      vendor/doctrine/lexer/composer.json
  46. 328 0
      vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php
  47. 17 0
      vendor/hg/apidoc/.github/workflows/mirror.yml
  48. 1 0
      vendor/hg/apidoc/.gitignore
  49. 21 0
      vendor/hg/apidoc/LICENSE
  50. 78 0
      vendor/hg/apidoc/README.md
  51. 42 0
      vendor/hg/apidoc/composer.json
  52. 44 0
      vendor/hg/apidoc/src/Auth.php
  53. 475 0
      vendor/hg/apidoc/src/Controller.php
  54. 317 0
      vendor/hg/apidoc/src/CreateCrud.php
  55. 457 0
      vendor/hg/apidoc/src/ParseAnnotation.php
  56. 53 0
      vendor/hg/apidoc/src/ParseMarkdown.php
  57. 160 0
      vendor/hg/apidoc/src/ParseModel.php
  58. 40 0
      vendor/hg/apidoc/src/Service.php
  59. 210 0
      vendor/hg/apidoc/src/Utils.php
  60. 45 0
      vendor/hg/apidoc/src/annotation/AddField.php
  61. 15 0
      vendor/hg/apidoc/src/annotation/Author.php
  62. 15 0
      vendor/hg/apidoc/src/annotation/Desc.php
  63. 15 0
      vendor/hg/apidoc/src/annotation/Field.php
  64. 15 0
      vendor/hg/apidoc/src/annotation/Group.php
  65. 41 0
      vendor/hg/apidoc/src/annotation/Header.php
  66. 15 0
      vendor/hg/apidoc/src/annotation/Method.php
  67. 28 0
      vendor/hg/apidoc/src/annotation/Param.php
  68. 56 0
      vendor/hg/apidoc/src/annotation/ParamBase.php
  69. 15 0
      vendor/hg/apidoc/src/annotation/ParamType.php
  70. 31 0
      vendor/hg/apidoc/src/annotation/Returned.php
  71. 19 0
      vendor/hg/apidoc/src/annotation/Route.php
  72. 77 0
      vendor/hg/apidoc/src/annotation/Rule.php
  73. 15 0
      vendor/hg/apidoc/src/annotation/Tag.php
  74. 15 0
      vendor/hg/apidoc/src/annotation/Title.php
  75. 15 0
      vendor/hg/apidoc/src/annotation/Url.php
  76. 15 0
      vendor/hg/apidoc/src/annotation/WithoutField.php
  77. 62 0
      vendor/hg/apidoc/src/config.php
  78. 3 2
      vendor/services.php
  79. 3 0
      vendor/symfony/class-loader/.gitignore
  80. 141 0
      vendor/symfony/class-loader/ApcClassLoader.php
  81. 36 0
      vendor/symfony/class-loader/CHANGELOG.md
  82. 444 0
      vendor/symfony/class-loader/ClassCollectionLoader.php
  83. 203 0
      vendor/symfony/class-loader/ClassLoader.php
  84. 156 0
      vendor/symfony/class-loader/ClassMapGenerator.php
  85. 19 0
      vendor/symfony/class-loader/LICENSE
  86. 68 0
      vendor/symfony/class-loader/MapClassLoader.php
  87. 93 0
      vendor/symfony/class-loader/Psr4ClassLoader.php
  88. 14 0
      vendor/symfony/class-loader/README.md
  89. 197 0
      vendor/symfony/class-loader/Tests/ApcClassLoaderTest.php
  90. 316 0
      vendor/symfony/class-loader/Tests/ClassCollectionLoaderTest.php
  91. 235 0
      vendor/symfony/class-loader/Tests/ClassLoaderTest.php
  92. 148 0
      vendor/symfony/class-loader/Tests/ClassMapGeneratorTest.php
  93. 17 0
      vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Bar.php
  94. 17 0
      vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Baz.php
  95. 17 0
      vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Foo.php
  96. 17 0
      vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/FooBar.php
  97. 6 0
      vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Bar.php
  98. 6 0
      vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Baz.php
  99. 6 0
      vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Foo.php
  100. 6 0
      vendor/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/ApcPrefixCollision/A/Bar.php

+ 2 - 1
composer.json

@@ -30,7 +30,8 @@
     "zoujingli/ip2region": "^1.0",
     "zoujingli/think-library": "6.0.x-dev",
     "zoujingli/wechat-developer": "^1.2",
-    "itxq/api-doc-php": "^1.1"
+    "itxq/api-doc-php": "^1.1",
+    "hg/apidoc": "^2.4"
   },
   "autoload": {
     "psr-0": {

+ 62 - 0
config/apidoc.php

@@ -0,0 +1,62 @@
+<?php
+return [
+    // 文档标题
+    'title'              => 'APi接口文档',
+    // 文档描述
+    'desc'               => '',
+    // 版权申明
+    'copyright'          => 'Powered By hg-code',
+    // 默认作者
+    'default_author'=>'',
+    // 默认请求类型
+    'default_method'=>'GET',
+    // 设置应用/版本(必须设置)
+    'apps'           => [
+        ['title'=>'v1.0','path'=>'app\controller','folder'=>'v1'],
+    ],
+    // 控制器分组
+    'groups'             => [],
+    // 指定公共注释定义的文件地址
+    'definitions'        => "app\controller\Definitions",
+    //指定生成文档的控制器
+    'controllers'        => [],
+    // 过滤,不解析的控制器
+    'filter_controllers' => [],
+    // 缓存配置
+    'cache'              => [
+        // 是否开启缓存
+        'enable' => false,
+        // 缓存文件路径
+        'path'   =>  '../runtime/apidoc/',
+        // 是否显示更新缓存按钮
+        'reload' => true,
+        // 最大缓存文件数
+        'max'    => 5,  //最大缓存数量
+    ],
+    // 权限认证配置
+    'auth'               => [
+        // 是否启用密码验证
+        'enable'     => false,
+        // 验证密码
+        'password'   => "123456",
+        // 密码加密盐
+        'secret_key' => "apidoc#hg_code",
+    ],
+    // 统一的请求Header
+    'headers'=>[],
+    // 统一的请求参数Parameters
+    'parameters'=>[],
+    // 统一的请求响应体,仅显示在文档提示中
+    'responses'=>[
+        ['name'=>'code','desc'=>'状态码','type'=>'int'],
+        ['name'=>'message','desc'=>'操作描述','type'=>'string'],
+        ['name'=>'data','desc'=>'业务数据','main'=>true,'type'=>'object'],
+    ],
+    // md文档
+    'docs'              => [
+        'menu_title' => '开发文档',
+        'menus'      => []
+    ],
+    'crud'=>[]
+
+];

BIN
public/apidoc/favicon.ico


+ 1 - 0
public/apidoc/index.html

@@ -0,0 +1 @@
+<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=favicon.ico><title>apidoc</title><link href=static/css/app.d65fcd89.css rel=preload as=style><link href=static/css/chunk-vendors.39c71c89.css rel=preload as=style><link href=static/js/app.9ecb1b87.js rel=preload as=script><link href=static/js/chunk-vendors.52db4364.js rel=preload as=script><link href=static/css/chunk-vendors.39c71c89.css rel=stylesheet><link href=static/css/app.d65fcd89.css rel=stylesheet></head><body><noscript><strong>We're sorry but apidoc doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=static/js/chunk-vendors.52db4364.js></script><script src=static/js/app.9ecb1b87.js></script></body></html>

BIN
public/apidoc/logo.png


File diff suppressed because it is too large
+ 0 - 0
public/apidoc/static/css/app.d65fcd89.css


File diff suppressed because it is too large
+ 0 - 0
public/apidoc/static/css/chunk-vendors.39c71c89.css


File diff suppressed because it is too large
+ 0 - 0
public/apidoc/static/js/app.9ecb1b87.js


File diff suppressed because it is too large
+ 0 - 0
public/apidoc/static/js/chunk-vendors.52db4364.js


+ 4 - 0
vendor/composer/autoload_psr4.php

@@ -10,11 +10,13 @@ return array(
     'think\\admin\\' => array($vendorDir . '/zoujingli/think-library/src'),
     'think\\' => array($vendorDir . '/topthink/framework/src/think', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/think-template/src'),
     'itxq\\apidoc\\' => array($vendorDir . '/itxq/api-doc-php/src'),
+    'hg\\apidoc\\' => array($vendorDir . '/hg/apidoc/src'),
     'app\\' => array($baseDir . '/app'),
     'WePay\\' => array($vendorDir . '/zoujingli/wechat-developer/WePay'),
     'WeMini\\' => array($vendorDir . '/zoujingli/wechat-developer/WeMini'),
     'WeChat\\' => array($vendorDir . '/zoujingli/wechat-developer/WeChat'),
     'Symfony\\Component\\OptionsResolver\\' => array($vendorDir . '/symfony/options-resolver'),
+    'Symfony\\Component\\ClassLoader\\' => array($vendorDir . '/symfony/class-loader'),
     'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
     'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
     'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
@@ -22,5 +24,7 @@ return array(
     'League\\Flysystem\\Cached\\' => array($vendorDir . '/league/flysystem-cached-adapter/src'),
     'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
     'Endroid\\QrCode\\' => array($vendorDir . '/endroid/qr-code/src'),
+    'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'),
+    'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations'),
     'AliPay\\' => array($vendorDir . '/zoujingli/wechat-developer/AliPay'),
 );

+ 26 - 0
vendor/composer/autoload_static.php

@@ -23,6 +23,10 @@ class ComposerStaticInit4f89fd0e0503ccf740f2fa5757825d7b
         array (
             'itxq\\apidoc\\' => 12,
         ),
+        'h' => 
+        array (
+            'hg\\apidoc\\' => 10,
+        ),
         'a' => 
         array (
             'app\\' => 4,
@@ -36,6 +40,7 @@ class ComposerStaticInit4f89fd0e0503ccf740f2fa5757825d7b
         'S' => 
         array (
             'Symfony\\Component\\OptionsResolver\\' => 34,
+            'Symfony\\Component\\ClassLoader\\' => 30,
         ),
         'P' => 
         array (
@@ -53,6 +58,11 @@ class ComposerStaticInit4f89fd0e0503ccf740f2fa5757825d7b
         array (
             'Endroid\\QrCode\\' => 15,
         ),
+        'D' => 
+        array (
+            'Doctrine\\Common\\Lexer\\' => 22,
+            'Doctrine\\Common\\Annotations\\' => 28,
+        ),
         'A' => 
         array (
             'AliPay\\' => 7,
@@ -79,6 +89,10 @@ class ComposerStaticInit4f89fd0e0503ccf740f2fa5757825d7b
         array (
             0 => __DIR__ . '/..' . '/itxq/api-doc-php/src',
         ),
+        'hg\\apidoc\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/hg/apidoc/src',
+        ),
         'app\\' => 
         array (
             0 => __DIR__ . '/../..' . '/app',
@@ -99,6 +113,10 @@ class ComposerStaticInit4f89fd0e0503ccf740f2fa5757825d7b
         array (
             0 => __DIR__ . '/..' . '/symfony/options-resolver',
         ),
+        'Symfony\\Component\\ClassLoader\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/class-loader',
+        ),
         'Psr\\SimpleCache\\' => 
         array (
             0 => __DIR__ . '/..' . '/psr/simple-cache/src',
@@ -127,6 +145,14 @@ class ComposerStaticInit4f89fd0e0503ccf740f2fa5757825d7b
         array (
             0 => __DIR__ . '/..' . '/endroid/qr-code/src',
         ),
+        'Doctrine\\Common\\Lexer\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/doctrine/lexer/lib/Doctrine/Common/Lexer',
+        ),
+        'Doctrine\\Common\\Annotations\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations',
+        ),
         'AliPay\\' => 
         array (
             0 => __DIR__ . '/..' . '/zoujingli/wechat-developer/AliPay',

+ 287 - 0
vendor/composer/installed.json

@@ -1,5 +1,163 @@
 [
     {
+        "name": "doctrine/annotations",
+        "version": "1.12.1",
+        "version_normalized": "1.12.1.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/doctrine/annotations.git",
+            "reference": "b17c5014ef81d212ac539f07a1001832df1b6d3b"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/doctrine/annotations/zipball/b17c5014ef81d212ac539f07a1001832df1b6d3b",
+            "reference": "b17c5014ef81d212ac539f07a1001832df1b6d3b",
+            "shasum": "",
+            "mirrors": [
+                {
+                    "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                    "preferred": true
+                }
+            ]
+        },
+        "require": {
+            "doctrine/lexer": "1.*",
+            "ext-tokenizer": "*",
+            "php": "^7.1 || ^8.0"
+        },
+        "require-dev": {
+            "doctrine/cache": "1.*",
+            "doctrine/coding-standard": "^6.0 || ^8.1",
+            "phpstan/phpstan": "^0.12.20",
+            "phpunit/phpunit": "^7.5 || ^9.1.5"
+        },
+        "time": "2021-02-21T21:00:45+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Guilherme Blanco",
+                "email": "guilhermeblanco@gmail.com"
+            },
+            {
+                "name": "Roman Borschel",
+                "email": "roman@code-factory.org"
+            },
+            {
+                "name": "Benjamin Eberlei",
+                "email": "kontakt@beberlei.de"
+            },
+            {
+                "name": "Jonathan Wage",
+                "email": "jonwage@gmail.com"
+            },
+            {
+                "name": "Johannes Schmitt",
+                "email": "schmittjoh@gmail.com"
+            }
+        ],
+        "description": "Docblock Annotations Parser",
+        "homepage": "https://www.doctrine-project.org/projects/annotations.html",
+        "keywords": [
+            "annotations",
+            "docblock",
+            "parser"
+        ]
+    },
+    {
+        "name": "doctrine/lexer",
+        "version": "1.2.1",
+        "version_normalized": "1.2.1.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/doctrine/lexer.git",
+            "reference": "e864bbf5904cb8f5bb334f99209b48018522f042"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042",
+            "reference": "e864bbf5904cb8f5bb334f99209b48018522f042",
+            "shasum": "",
+            "mirrors": [
+                {
+                    "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                    "preferred": true
+                }
+            ]
+        },
+        "require": {
+            "php": "^7.2 || ^8.0"
+        },
+        "require-dev": {
+            "doctrine/coding-standard": "^6.0",
+            "phpstan/phpstan": "^0.11.8",
+            "phpunit/phpunit": "^8.2"
+        },
+        "time": "2020-05-25T17:44:05+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.2.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Guilherme Blanco",
+                "email": "guilhermeblanco@gmail.com"
+            },
+            {
+                "name": "Roman Borschel",
+                "email": "roman@code-factory.org"
+            },
+            {
+                "name": "Johannes Schmitt",
+                "email": "schmittjoh@gmail.com"
+            }
+        ],
+        "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
+        "homepage": "https://www.doctrine-project.org/projects/lexer.html",
+        "keywords": [
+            "annotations",
+            "docblock",
+            "lexer",
+            "parser",
+            "php"
+        ],
+        "funding": [
+            {
+                "url": "https://www.doctrine-project.org/sponsorship.html",
+                "type": "custom"
+            },
+            {
+                "url": "https://www.patreon.com/phpdoctrine",
+                "type": "patreon"
+            },
+            {
+                "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
+                "type": "tidelift"
+            }
+        ]
+    },
+    {
         "name": "endroid/qr-code",
         "version": "1.9.3",
         "version_normalized": "1.9.3.0",
@@ -72,6 +230,71 @@
         }
     },
     {
+        "name": "hg/apidoc",
+        "version": "v2.4.0",
+        "version_normalized": "2.4.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/HGthecode/thinkphp-apidoc.git",
+            "reference": "b61f91453628683d4a9ab487cb8ed0384fac82de"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/HGthecode/thinkphp-apidoc/zipball/b61f91453628683d4a9ab487cb8ed0384fac82de",
+            "reference": "b61f91453628683d4a9ab487cb8ed0384fac82de",
+            "shasum": "",
+            "mirrors": [
+                {
+                    "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                    "preferred": true
+                }
+            ]
+        },
+        "require": {
+            "doctrine/annotations": "^1.6",
+            "symfony/class-loader": "~3.2.0"
+        },
+        "time": "2021-03-18T08:02:09+00:00",
+        "type": "think-extend",
+        "extra": {
+            "think": {
+                "services": [
+                    "hg\\apidoc\\Service"
+                ],
+                "config": {
+                    "apidoc": "src/config.php"
+                }
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "hg\\apidoc\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "hg-code",
+                "email": "376401263@qq.com"
+            }
+        ],
+        "description": "thinkphp API文档自动生成",
+        "keywords": [
+            "apidoc",
+            "api文档",
+            "php接口文档",
+            "swagger",
+            "thinkphp",
+            "接口文档",
+            "注释生成",
+            "自动生成"
+        ]
+    },
+    {
         "name": "itxq/api-doc-php",
         "version": "v1.1.1",
         "version_normalized": "1.1.1.0",
@@ -508,6 +731,70 @@
         }
     },
     {
+        "name": "symfony/class-loader",
+        "version": "v3.2.14",
+        "version_normalized": "3.2.14.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/class-loader.git",
+            "reference": "e192d96b15fdd168bdb1c91001d26c93ba4af482"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/class-loader/zipball/e192d96b15fdd168bdb1c91001d26c93ba4af482",
+            "reference": "e192d96b15fdd168bdb1c91001d26c93ba4af482",
+            "shasum": "",
+            "mirrors": [
+                {
+                    "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                    "preferred": true
+                }
+            ]
+        },
+        "require": {
+            "php": ">=5.5.9"
+        },
+        "require-dev": {
+            "symfony/finder": "~2.8|~3.0",
+            "symfony/polyfill-apcu": "~1.1"
+        },
+        "suggest": {
+            "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM"
+        },
+        "time": "2017-06-01T21:00:24+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "3.2-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Symfony\\Component\\ClassLoader\\": ""
+            },
+            "exclude-from-classmap": [
+                "/Tests/"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony ClassLoader Component",
+        "homepage": "https://symfony.com"
+    },
+    {
         "name": "symfony/options-resolver",
         "version": "v3.4.47",
         "version_normalized": "3.4.47.0",

+ 162 - 0
vendor/doctrine/annotations/CHANGELOG.md

@@ -0,0 +1,162 @@
+## Changelog
+
+### 1.6.1
+
+This release fixes an issue in which annotations such as `@foo-bar`
+and `@foo-` were incorrectly recognised as valid, and both erroneously
+parsed as `@foo`.
+
+Any annotation with `@name-*` format will now silently be ignored,
+allowing vendor-specific annotations to be prefixed with the tool
+name.
+
+Total issues resolved: **3**
+
+- [165: Update the composer branch alias](https://github.com/doctrine/annotations/pull/165) thanks to @mikeSimonson
+- [209: Change Annotation::value typehint to mixed](https://github.com/doctrine/annotations/pull/209) thanks to @malarzm
+- [257: Skip parsing annotations containing dashes, such as `@Foo-bar`, or `@Foo-`](https://github.com/doctrine/annotations/pull/257) thanks to @Ocramius
+
+### 1.6.0
+
+This release brings a new endpoint that make sure that you can't shoot yourself in the foot by calling ```registerLoader``` multiple times and a few tests improvements.
+
+Total issues resolved: **7**
+
+- [145: Memory leak in AnnotationRegistry::registerLoader() when called multiple times](https://github.com/doctrine/annotations/issues/145) thanks to @TriAnMan
+- [146: Import error on @experimental Annotation](https://github.com/doctrine/annotations/issues/146) thanks to @aturki
+- [147: Ignoring @experimental annotation used by Symfony 3.3 CacheAdapter](https://github.com/doctrine/annotations/pull/147) thanks to @aturki
+- [151: Remove duplicate code in `DCOM58Test`](https://github.com/doctrine/annotations/pull/151) thanks to @tuanphpvn
+- [161: Prevent loading class&#95;exists multiple times](https://github.com/doctrine/annotations/pull/161) thanks to @jrjohnson
+- [162: Add registerUniqueLoader to AnnotationRegistry](https://github.com/doctrine/annotations/pull/162) thanks to @jrjohnson
+- [163: Use assertDirectoryExists and assertDirectoryNotExists](https://github.com/doctrine/annotations/pull/163) thanks to @carusogabriel
+
+Thanks to everyone involved in this release.
+
+### 1.5.0
+
+This release increments the minimum supported PHP version to 7.1.0.
+
+Also, HHVM official support has been dropped.
+
+Some noticeable performance improvements to annotation autoloading
+have been applied, making failed annotation autoloading less heavy
+on the filesystem access.
+
+- [133: Add @throws annotation in AnnotationReader#__construct()](https://github.com/doctrine/annotations/issues/133) thanks to @SenseException
+- [134: Require PHP 7.1, drop HHVM support](https://github.com/doctrine/annotations/issues/134) thanks to @lcobucci
+- [135: Prevent the same loader from being registered twice](https://github.com/doctrine/annotations/issues/135)  thanks to @jrjohnson
+- [137: #135 optimise multiple class load attempts in AnnotationRegistry](https://github.com/doctrine/annotations/issues/137)  thanks to @Ocramius
+
+
+### 1.4.0
+
+This release fix an issue were some annotations could be not loaded if the namespace in the use statement started with a backslash.
+It also update the tests and drop the support for php 5.X
+
+- [115: Missing annotations with the latest composer version](https://github.com/doctrine/annotations/issues/115) thanks to @pascalporedda
+- [120: Missing annotations with the latest composer version](https://github.com/doctrine/annotations/pull/120) thanks to @gnat42
+- [121: Adding a more detailed explanation of the test](https://github.com/doctrine/annotations/pull/121) thanks to @mikeSimonson
+- [101: Test annotation parameters containing space](https://github.com/doctrine/annotations/pull/101) thanks to @mikeSimonson
+- [111: Cleanup: move to correct phpunit assertions](https://github.com/doctrine/annotations/pull/111) thanks to @Ocramius
+- [112: Removes support for PHP 5.x](https://github.com/doctrine/annotations/pull/112) thanks to @railto
+- [113: bumped phpunit version to 5.7](https://github.com/doctrine/annotations/pull/113) thanks to @gabbydgab
+- [114: Enhancement: Use SVG Travis build badge](https://github.com/doctrine/annotations/pull/114) thanks to @localheinz
+- [118: Integrating PHPStan](https://github.com/doctrine/annotations/pull/118) thanks to @ondrejmirtes
+
+### 1.3.1 - 2016-12-30
+
+This release fixes an issue with ignored annotations that were already
+autoloaded, causing the `SimpleAnnotationReader` to pick them up
+anyway. [#110](https://github.com/doctrine/annotations/pull/110)
+
+Additionally, an issue was fixed in the `CachedReader`, which was
+not correctly checking the freshness of cached annotations when
+traits were defined on a class. [#105](https://github.com/doctrine/annotations/pull/105)
+
+Total issues resolved: **2**
+
+- [105: Return single max timestamp](https://github.com/doctrine/annotations/pull/105)
+- [110: setIgnoreNotImportedAnnotations(true) didn&rsquo;t work for existing classes](https://github.com/doctrine/annotations/pull/110)
+
+### 1.3.0
+
+This release introduces a PHP version bump. `doctrine/annotations` now requires PHP
+5.6 or later to be installed.
+
+A series of additional improvements have been introduced:
+
+ * support for PHP 7 "grouped use statements"
+ * support for ignoring entire namespace names
+   via `Doctrine\Common\Annotations\AnnotationReader::addGlobalIgnoredNamespace()` and
+   `Doctrine\Common\Annotations\DocParser::setIgnoredAnnotationNamespaces()`. This will
+   allow you to ignore annotations from namespaces that you cannot autoload
+ * testing all parent classes and interfaces when checking if the annotation cache
+   in the `CachedReader` is fresh
+ * simplifying the cache keys used by the `CachedReader`: keys are no longer artificially
+   namespaced, since `Doctrine\Common\Cache` already supports that
+ * corrected parsing of multibyte strings when `mbstring.func_overload` is enabled
+ * corrected parsing of annotations when `"\t"` is put before the first annotation
+   in a docblock
+ * allow skipping non-imported annotations when a custom `DocParser` is passed to
+   the `AnnotationReader` constructor
+
+Total issues resolved: **15**
+
+- [45: DocParser can now ignore whole namespaces](https://github.com/doctrine/annotations/pull/45)
+- [57: Switch to the docker-based infrastructure on Travis](https://github.com/doctrine/annotations/pull/57)
+- [59: opcache.load&#95;comments has been removed from PHP 7](https://github.com/doctrine/annotations/pull/59)
+- [62: &#91;CachedReader&#92; Test traits and parent class to see if cache is fresh](https://github.com/doctrine/annotations/pull/62)
+- [65: Remove cache salt making key unnecessarily long](https://github.com/doctrine/annotations/pull/65)
+- [66: Fix of incorrect parsing multibyte strings](https://github.com/doctrine/annotations/pull/66)
+- [68: Annotations that are indented by tab are not processed.](https://github.com/doctrine/annotations/issues/68)
+- [69: Support for Group Use Statements](https://github.com/doctrine/annotations/pull/69)
+- [70: Allow tab character before first annotation in DocBlock](https://github.com/doctrine/annotations/pull/70)
+- [74: Ignore not registered annotations fix](https://github.com/doctrine/annotations/pull/74)
+- [92: Added tests for AnnotationRegistry class.](https://github.com/doctrine/annotations/pull/92)
+- [96: Fix/#62 check trait and parent class ttl in annotations](https://github.com/doctrine/annotations/pull/96)
+- [97: Feature - #45 - allow ignoring entire namespaces](https://github.com/doctrine/annotations/pull/97)
+- [98: Enhancement/#65 remove cache salt from cached reader](https://github.com/doctrine/annotations/pull/98)
+- [99: Fix - #70 - allow tab character before first annotation in docblock](https://github.com/doctrine/annotations/pull/99)
+
+### 1.2.4
+
+Total issues resolved: **1**
+
+- [51: FileCacheReader::saveCacheFile::unlink fix](https://github.com/doctrine/annotations/pull/51)
+
+### 1.2.3
+
+Total issues resolved: [**2**](https://github.com/doctrine/annotations/milestones/v1.2.3)
+
+- [49: #46 - applying correct `chmod()` to generated cache file](https://github.com/doctrine/annotations/pull/49)
+- [50: Hotfix: match escaped quotes (revert #44)](https://github.com/doctrine/annotations/pull/50)
+
+### 1.2.2
+
+Total issues resolved: **4**
+
+- [43: Exclude files from distribution with .gitattributes](https://github.com/doctrine/annotations/pull/43)
+- [44: Update DocLexer.php](https://github.com/doctrine/annotations/pull/44)
+- [46: A plain &quot;file&#95;put&#95;contents&quot; can cause havoc](https://github.com/doctrine/annotations/pull/46)
+- [48: Deprecating the `FileCacheReader` in 1.2.2: will be removed in 2.0.0](https://github.com/doctrine/annotations/pull/48)
+
+### 1.2.1
+
+Total issues resolved: **4**
+
+- [38: fixes doctrine/common#326](https://github.com/doctrine/annotations/pull/38)
+- [39: Remove superfluous NS](https://github.com/doctrine/annotations/pull/39)
+- [41: Warn if load_comments is not enabled.](https://github.com/doctrine/annotations/pull/41)
+- [42: Clean up unused uses](https://github.com/doctrine/annotations/pull/42)
+
+### 1.2.0
+
+ * HHVM support
+ * Allowing dangling comma in annotations
+ * Excluded annotations are no longer autoloaded
+ * Importing namespaces also in traits
+ * Added support for `::class` 5.5-style constant, works also in 5.3 and 5.4
+
+### 1.1.0
+
+ * Add Exception when ZendOptimizer+ or Opcache is configured to drop comments

+ 19 - 0
vendor/doctrine/annotations/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2006-2013 Doctrine Project
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 22 - 0
vendor/doctrine/annotations/README.md

@@ -0,0 +1,22 @@
+# Doctrine Annotations
+
+[![Build Status](https://github.com/doctrine/annotations/workflows/Continuous%20Integration/badge.svg?label=build)](https://github.com/doctrine/persistence/actions)
+[![Dependency Status](https://www.versioneye.com/package/php--doctrine--annotations/badge.png)](https://www.versioneye.com/package/php--doctrine--annotations)
+[![Reference Status](https://www.versioneye.com/php/doctrine:annotations/reference_badge.svg)](https://www.versioneye.com/php/doctrine:annotations/references)
+[![Total Downloads](https://poser.pugx.org/doctrine/annotations/downloads.png)](https://packagist.org/packages/doctrine/annotations)
+[![Latest Stable Version](https://img.shields.io/packagist/v/doctrine/annotations.svg?label=stable)](https://packagist.org/packages/doctrine/annotations)
+
+Docblock Annotations Parser library (extracted from [Doctrine Common](https://github.com/doctrine/common)).
+
+## Documentation
+
+See the [doctrine-project website](https://www.doctrine-project.org/projects/doctrine-annotations/en/latest/index.html).
+
+## Contributing
+
+When making a pull request, make sure your changes follow the
+[Coding Standard Guidelines](https://www.doctrine-project.org/projects/doctrine-coding-standard/en/current/reference/index.html#introduction).
+
+## Changelog
+
+See [CHANGELOG.md](CHANGELOG.md).

+ 42 - 0
vendor/doctrine/annotations/composer.json

@@ -0,0 +1,42 @@
+{
+    "name": "doctrine/annotations",
+    "type": "library",
+    "description": "Docblock Annotations Parser",
+    "keywords": ["annotations", "docblock", "parser"],
+    "homepage": "https://www.doctrine-project.org/projects/annotations.html",
+    "license": "MIT",
+    "authors": [
+        {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
+        {"name": "Roman Borschel", "email": "roman@code-factory.org"},
+        {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
+        {"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
+        {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
+    ],
+    "require": {
+        "php": "^7.1 || ^8.0",
+        "ext-tokenizer": "*",
+        "doctrine/lexer": "1.*"
+    },
+    "require-dev": {
+        "doctrine/cache": "1.*",
+        "doctrine/coding-standard": "^6.0 || ^8.1",
+        "phpstan/phpstan": "^0.12.20",
+        "phpunit/phpunit": "^7.5 || ^9.1.5"
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "autoload": {
+        "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Doctrine\\Performance\\Common\\Annotations\\": "tests/Doctrine/Performance/Common/Annotations",
+            "Doctrine\\Tests\\Common\\Annotations\\": "tests/Doctrine/Tests/Common/Annotations"
+        },
+        "files": [
+            "tests/Doctrine/Tests/Common/Annotations/Fixtures/functions.php",
+            "tests/Doctrine/Tests/Common/Annotations/Fixtures/SingleClassLOC1000.php"
+        ]
+    }
+}

+ 271 - 0
vendor/doctrine/annotations/docs/en/annotations.rst

@@ -0,0 +1,271 @@
+Handling Annotations
+====================
+
+There are several different approaches to handling annotations in PHP.
+Doctrine Annotations maps docblock annotations to PHP classes. Because
+not all docblock annotations are used for metadata purposes a filter is
+applied to ignore or skip classes that are not Doctrine annotations.
+
+Take a look at the following code snippet:
+
+.. code-block:: php
+
+    namespace MyProject\Entities;
+
+    use Doctrine\ORM\Mapping AS ORM;
+    use Symfony\Component\Validator\Constraints AS Assert;
+
+    /**
+     * @author Benjamin Eberlei
+     * @ORM\Entity
+     * @MyProject\Annotations\Foobarable
+     */
+    class User
+    {
+        /**
+         * @ORM\Id @ORM\Column @ORM\GeneratedValue
+         * @dummy
+         * @var int
+         */
+        private $id;
+
+        /**
+         * @ORM\Column(type="string")
+         * @Assert\NotEmpty
+         * @Assert\Email
+         * @var string
+         */
+        private $email;
+    }
+
+In this snippet you can see a variety of different docblock annotations:
+
+- Documentation annotations such as ``@var`` and ``@author``. These
+  annotations are ignored and never considered for throwing an
+  exception due to wrongly used annotations.
+- Annotations imported through use statements. The statement ``use
+  Doctrine\ORM\Mapping AS ORM`` makes all classes under that namespace
+  available as ``@ORM\ClassName``. Same goes for the import of
+  ``@Assert``.
+- The ``@dummy`` annotation. It is not a documentation annotation and
+  not ignored. For Doctrine Annotations it is not entirely clear how
+  to handle this annotation. Depending on the configuration an exception
+  (unknown annotation) will be thrown when parsing this annotation.
+- The fully qualified annotation ``@MyProject\Annotations\Foobarable``.
+  This is transformed directly into the given class name.
+
+How are these annotations loaded? From looking at the code you could
+guess that the ORM Mapping, Assert Validation and the fully qualified
+annotation can just be loaded using
+the defined PHP autoloaders. This is not the case however: For error
+handling reasons every check for class existence inside the
+``AnnotationReader`` sets the second parameter $autoload
+of ``class_exists($name, $autoload)`` to false. To work flawlessly the
+``AnnotationReader`` requires silent autoloaders which many autoloaders are
+not. Silent autoloading is NOT part of the `PSR-0 specification
+<https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md>`_
+for autoloading.
+
+This is why Doctrine Annotations uses its own autoloading mechanism
+through a global registry. If you are wondering about the annotation
+registry being global, there is no other way to solve the architectural
+problems of autoloading annotation classes in a straightforward fashion.
+Additionally if you think about PHP autoloading then you recognize it is
+a global as well.
+
+To anticipate the configuration section, making the above PHP class work
+with Doctrine Annotations requires this setup:
+
+.. code-block:: php
+
+    use Doctrine\Common\Annotations\AnnotationReader;
+    use Doctrine\Common\Annotations\AnnotationRegistry;
+
+    AnnotationRegistry::registerFile("/path/to/doctrine/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php");
+    AnnotationRegistry::registerAutoloadNamespace("Symfony\Component\Validator\Constraint", "/path/to/symfony/src");
+    AnnotationRegistry::registerAutoloadNamespace("MyProject\Annotations", "/path/to/myproject/src");
+
+    $reader = new AnnotationReader();
+    AnnotationReader::addGlobalIgnoredName('dummy');
+
+The second block with the annotation registry calls registers all the
+three different annotation namespaces that are used.
+Doctrine Annotations saves all its annotations in a single file, that is
+why ``AnnotationRegistry#registerFile`` is used in contrast to
+``AnnotationRegistry#registerAutoloadNamespace`` which creates a PSR-0
+compatible loading mechanism for class to file names.
+
+In the third block, we create the actual ``AnnotationReader`` instance.
+Note that we also add ``dummy`` to the global list of ignored
+annotations for which we do not throw exceptions. Setting this is
+necessary in our example case, otherwise ``@dummy`` would trigger an
+exception to be thrown during the parsing of the docblock of
+``MyProject\Entities\User#id``.
+
+Setup and Configuration
+-----------------------
+
+To use the annotations library is simple, you just need to create a new
+``AnnotationReader`` instance:
+
+.. code-block:: php
+
+    $reader = new \Doctrine\Common\Annotations\AnnotationReader();
+
+This creates a simple annotation reader with no caching other than in
+memory (in php arrays). Since parsing docblocks can be expensive you
+should cache this process by using a caching reader.
+
+You can use a file caching reader, but please note it is deprecated to
+do so:
+
+.. code-block:: php
+
+    use Doctrine\Common\Annotations\FileCacheReader;
+    use Doctrine\Common\Annotations\AnnotationReader;
+
+    $reader = new FileCacheReader(
+        new AnnotationReader(),
+        "/path/to/cache",
+        $debug = true
+    );
+
+If you set the ``debug`` flag to ``true`` the cache reader will check
+for changes in the original files, which is very important during
+development. If you don't set it to ``true`` you have to delete the
+directory to clear the cache. This gives faster performance, however
+should only be used in production, because of its inconvenience during
+development.
+
+You can also use one of the ``Doctrine\Common\Cache\Cache`` cache
+implementations to cache the annotations:
+
+.. code-block:: php
+
+    use Doctrine\Common\Annotations\AnnotationReader;
+    use Doctrine\Common\Annotations\CachedReader;
+    use Doctrine\Common\Cache\ApcCache;
+
+    $reader = new CachedReader(
+        new AnnotationReader(),
+        new ApcCache(),
+        $debug = true
+    );
+
+The ``debug`` flag is used here as well to invalidate the cache files
+when the PHP class with annotations changed and should be used during
+development.
+
+.. warning ::
+
+    The ``AnnotationReader`` works and caches under the
+    assumption that all annotations of a doc-block are processed at
+    once. That means that annotation classes that do not exist and
+    aren't loaded and cannot be autoloaded (using the
+    AnnotationRegistry) would never be visible and not accessible if a
+    cache is used unless the cache is cleared and the annotations
+    requested again, this time with all annotations defined.
+
+By default the annotation reader returns a list of annotations with
+numeric indexes. If you want your annotations to be indexed by their
+class name you can wrap the reader in an ``IndexedReader``:
+
+.. code-block:: php
+
+    use Doctrine\Common\Annotations\AnnotationReader;
+    use Doctrine\Common\Annotations\IndexedReader;
+
+    $reader = new IndexedReader(new AnnotationReader());
+
+.. warning::
+
+    You should never wrap the indexed reader inside a cached reader,
+    only the other way around. This way you can re-use the cache with
+    indexed or numeric keys, otherwise your code may experience failures
+    due to caching in a numerical or indexed format.
+
+Registering Annotations
+~~~~~~~~~~~~~~~~~~~~~~~
+
+As explained in the introduction, Doctrine Annotations uses its own
+autoloading mechanism to determine if a given annotation has a
+corresponding PHP class that can be autoloaded. For annotation
+autoloading you have to configure the
+``Doctrine\Common\Annotations\AnnotationRegistry``. There are three
+different mechanisms to configure annotation autoloading:
+
+- Calling ``AnnotationRegistry#registerFile($file)`` to register a file
+  that contains one or more annotation classes.
+- Calling ``AnnotationRegistry#registerNamespace($namespace, $dirs =
+  null)`` to register that the given namespace contains annotations and
+  that their base directory is located at the given $dirs or in the
+  include path if ``NULL`` is passed. The given directories should *NOT*
+  be the directory where classes of the namespace are in, but the base
+  directory of the root namespace. The AnnotationRegistry uses a
+  namespace to directory separator approach to resolve the correct path.
+- Calling ``AnnotationRegistry#registerLoader($callable)`` to register
+  an autoloader callback. The callback accepts the class as first and
+  only parameter and has to return ``true`` if the corresponding file
+  was found and included.
+
+.. note::
+
+    Loaders have to fail silently, if a class is not found even if it
+    matches for example the namespace prefix of that loader. Never is a
+    loader to throw a warning or exception if the loading failed
+    otherwise parsing doc block annotations will become a huge pain.
+
+A sample loader callback could look like:
+
+.. code-block:: php
+
+    use Doctrine\Common\Annotations\AnnotationRegistry;
+    use Symfony\Component\ClassLoader\UniversalClassLoader;
+
+    AnnotationRegistry::registerLoader(function($class) {
+        $file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php";
+
+        if (file_exists("/my/base/path/" . $file)) {
+            // file_exists() makes sure that the loader fails silently
+            require "/my/base/path/" . $file;
+        }
+    });
+
+    $loader = new UniversalClassLoader();
+    AnnotationRegistry::registerLoader(array($loader, "loadClass"));
+
+
+Ignoring missing exceptions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By default an exception is thrown from the ``AnnotationReader`` if an
+annotation was found that:
+
+- is not part of the list of ignored "documentation annotations";
+- was not imported through a use statement;
+- is not a fully qualified class that exists.
+
+You can disable this behavior for specific names if your docblocks do
+not follow strict requirements:
+
+.. code-block:: php
+
+    $reader = new \Doctrine\Common\Annotations\AnnotationReader();
+    AnnotationReader::addGlobalIgnoredName('foo');
+
+PHP Imports
+~~~~~~~~~~~
+
+By default the annotation reader parses the use-statement of a php file
+to gain access to the import rules and register them for the annotation
+processing. Only if you are using PHP Imports can you validate the
+correct usage of annotations and throw exceptions if you misspelled an
+annotation. This mechanism is enabled by default.
+
+To ease the upgrade path, we still allow you to disable this mechanism.
+Note however that we will remove this in future versions:
+
+.. code-block:: php
+
+    $reader = new \Doctrine\Common\Annotations\AnnotationReader();
+    $reader->setEnabledPhpImports(false);

+ 443 - 0
vendor/doctrine/annotations/docs/en/custom.rst

@@ -0,0 +1,443 @@
+Custom Annotation Classes
+=========================
+
+If you want to define your own annotations, you just have to group them
+in a namespace and register this namespace in the ``AnnotationRegistry``.
+Annotation classes have to contain a class-level docblock with the text
+``@Annotation``:
+
+.. code-block:: php
+
+    namespace MyCompany\Annotations;
+
+    /** @Annotation */
+    class Bar
+    {
+        // some code
+    }
+
+Inject annotation values
+------------------------
+
+The annotation parser checks if the annotation constructor has arguments,
+if so then it will pass the value array, otherwise it will try to inject
+values into public properties directly:
+
+
+.. code-block:: php
+
+    namespace MyCompany\Annotations;
+
+    /**
+     * @Annotation
+     *
+     * Some Annotation using a constructor
+     */
+    class Bar
+    {
+        private $foo;
+
+        public function __construct(array $values)
+        {
+            $this->foo = $values['foo'];
+        }
+    }
+
+    /**
+     * @Annotation
+     *
+     * Some Annotation without a constructor
+     */
+    class Foo
+    {
+        public $bar;
+    }
+
+Optional: Constructors with Named Parameters
+--------------------------------------------
+
+Starting with Annotations v1.11 a new annotation instantiation strategy
+is available that aims at compatibility of Annotation classes with the PHP 8
+attribute feature. You need to declare a constructor with regular parameter 
+names that match the named arguments in the annotation syntax.
+
+To enable this feature, you can tag your annotation class with 
+``@NamedArgumentConstructor`` (available from v1.12) or implement the
+``Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation`` interface
+(available from v1.11 and deprecated as of v1.12).
+When using the ``@NamedArgumentConstructor`` tag, the first argument of the
+constructor is considered as the default one.
+
+
+Usage with the ``@NamedArgumentContrustor`` tag
+
+.. code-block:: php
+
+    namespace MyCompany\Annotations;
+
+    /** 
+     * @Annotation 
+     * @NamedArgumentConstructor
+     */
+    class Bar implements NamedArgumentConstructorAnnotation
+    {
+        private $foo;
+
+        public function __construct(string $foo)
+        {
+            $this->foo = $foo;
+        }
+    }
+
+    /** Usable with @Bar(foo="baz") */
+    /** Usable with @Bar("baz") */
+
+In combination with PHP 8's constructor property promotion feature
+you can simplify this to:
+
+.. code-block:: php
+
+    namespace MyCompany\Annotations;
+
+    /** 
+     * @Annotation 
+     * @NamedArgumentConstructor
+     */
+    class Bar implements NamedArgumentConstructorAnnotation
+    {
+        public function __construct(private string $foo) {}
+    }
+
+
+Usage with the 
+``Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation``
+interface (v1.11, deprecated as of v1.12):
+.. code-block:: php
+
+    namespace MyCompany\Annotations;
+
+    use Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation;
+
+    /** @Annotation */
+    class Bar implements NamedArgumentConstructorAnnotation
+    {
+        private $foo;
+
+        public function __construct(private string $foo) {}
+    }
+
+    /** Usable with @Bar(foo="baz") */
+
+Annotation Target
+-----------------
+
+``@Target`` indicates the kinds of class elements to which an annotation
+type is applicable. Then you could define one or more targets:
+
+-  ``CLASS`` Allowed in class docblocks
+-  ``PROPERTY`` Allowed in property docblocks
+-  ``METHOD`` Allowed in the method docblocks
+-  ``FUNCTION`` Allowed in function dockblocks
+-  ``ALL`` Allowed in class, property, method and function docblocks
+-  ``ANNOTATION`` Allowed inside other annotations
+
+If the annotations is not allowed in the current context, an
+``AnnotationException`` is thrown.
+
+.. code-block:: php
+
+    namespace MyCompany\Annotations;
+
+    /**
+     * @Annotation
+     * @Target({"METHOD","PROPERTY"})
+     */
+    class Bar
+    {
+        // some code
+    }
+
+    /**
+     * @Annotation
+     * @Target("CLASS")
+     */
+    class Foo
+    {
+        // some code
+    }
+
+Attribute types
+---------------
+
+The annotation parser checks the given parameters using the phpdoc
+annotation ``@var``, The data type could be validated using the ``@var``
+annotation on the annotation properties or using the ``@Attributes`` and
+``@Attribute`` annotations.
+
+If the data type does not match you get an ``AnnotationException``
+
+.. code-block:: php
+
+    namespace MyCompany\Annotations;
+
+    /**
+     * @Annotation
+     * @Target({"METHOD","PROPERTY"})
+     */
+    class Bar
+    {
+        /** @var mixed */
+        public $mixed;
+
+        /** @var boolean */
+        public $boolean;
+
+        /** @var bool */
+        public $bool;
+
+        /** @var float */
+        public $float;
+
+        /** @var string */
+        public $string;
+
+        /** @var integer */
+        public $integer;
+
+        /** @var array */
+        public $array;
+
+        /** @var SomeAnnotationClass */
+        public $annotation;
+
+        /** @var array<integer> */
+        public $arrayOfIntegers;
+
+        /** @var array<SomeAnnotationClass> */
+        public $arrayOfAnnotations;
+    }
+
+    /**
+     * @Annotation
+     * @Target({"METHOD","PROPERTY"})
+     * @Attributes({
+     *   @Attribute("stringProperty", type = "string"),
+     *   @Attribute("annotProperty",  type = "SomeAnnotationClass"),
+     * })
+     */
+    class Foo
+    {
+        public function __construct(array $values)
+        {
+            $this->stringProperty = $values['stringProperty'];
+            $this->annotProperty = $values['annotProperty'];
+        }
+
+        // some code
+    }
+
+Annotation Required
+-------------------
+
+``@Required`` indicates that the field must be specified when the
+annotation is used. If it is not used you get an ``AnnotationException``
+stating that this value can not be null.
+
+Declaring a required field:
+
+.. code-block:: php
+
+    /**
+     * @Annotation
+     * @Target("ALL")
+     */
+    class Foo
+    {
+        /** @Required */
+        public $requiredField;
+    }
+
+Usage:
+
+.. code-block:: php
+
+    /** @Foo(requiredField="value") */
+    public $direction;                  // Valid
+
+     /** @Foo */
+    public $direction;                  // Required field missing, throws an AnnotationException
+
+
+Enumerated values
+-----------------
+
+- An annotation property marked with ``@Enum`` is a field that accepts a
+  fixed set of scalar values.
+- You should use ``@Enum`` fields any time you need to represent fixed
+  values.
+- The annotation parser checks the given value and throws an
+  ``AnnotationException`` if the value does not match.
+
+
+Declaring an enumerated property:
+
+.. code-block:: php
+
+    /**
+     * @Annotation
+     * @Target("ALL")
+     */
+    class Direction
+    {
+        /**
+         * @Enum({"NORTH", "SOUTH", "EAST", "WEST"})
+         */
+        public $value;
+    }
+
+Annotation usage:
+
+.. code-block:: php
+
+    /** @Direction("NORTH") */
+    public $direction;                  // Valid value
+
+     /** @Direction("NORTHEAST") */
+    public $direction;                  // Invalid value, throws an AnnotationException
+
+
+Constants
+---------
+
+The use of constants and class constants is available on the annotations
+parser.
+
+The following usages are allowed:
+
+.. code-block:: php
+
+    namespace MyCompany\Entity;
+
+    use MyCompany\Annotations\Foo;
+    use MyCompany\Annotations\Bar;
+    use MyCompany\Entity\SomeClass;
+
+    /**
+     * @Foo(PHP_EOL)
+     * @Bar(Bar::FOO)
+     * @Foo({SomeClass::FOO, SomeClass::BAR})
+     * @Bar({SomeClass::FOO_KEY = SomeClass::BAR_VALUE})
+     */
+    class User
+    {
+    }
+
+
+Be careful with constants and the cache !
+
+.. note::
+
+    The cached reader will not re-evaluate each time an annotation is
+    loaded from cache. When a constant is changed the cache must be
+    cleaned.
+
+
+Usage
+-----
+
+Using the library API is simple. Using the annotations described in the
+previous section, you can now annotate other classes with your
+annotations:
+
+.. code-block:: php
+
+    namespace MyCompany\Entity;
+
+    use MyCompany\Annotations\Foo;
+    use MyCompany\Annotations\Bar;
+
+    /**
+     * @Foo(bar="foo")
+     * @Bar(foo="bar")
+     */
+    class User
+    {
+    }
+
+Now we can write a script to get the annotations above:
+
+.. code-block:: php
+
+    $reflClass = new ReflectionClass('MyCompany\Entity\User');
+    $classAnnotations = $reader->getClassAnnotations($reflClass);
+
+    foreach ($classAnnotations AS $annot) {
+        if ($annot instanceof \MyCompany\Annotations\Foo) {
+            echo $annot->bar; // prints "foo";
+        } else if ($annot instanceof \MyCompany\Annotations\Bar) {
+            echo $annot->foo; // prints "bar";
+        }
+    }
+
+You have a complete API for retrieving annotation class instances from a
+class, property or method docblock:
+
+
+Reader API
+~~~~~~~~~~
+
+Access all annotations of a class
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+    public function getClassAnnotations(\ReflectionClass $class);
+
+Access one annotation of a class
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+    public function getClassAnnotation(\ReflectionClass $class, $annotationName);
+
+Access all annotations of a method
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+    public function getMethodAnnotations(\ReflectionMethod $method);
+
+Access one annotation of a method
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+    public function getMethodAnnotation(\ReflectionMethod $method, $annotationName);
+
+Access all annotations of a property
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+    public function getPropertyAnnotations(\ReflectionProperty $property);
+
+Access one annotation of a property
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+    public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName);
+
+Access all annotations of a function
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+    public function getFunctionAnnotations(\ReflectionFunction $property);
+
+Access one annotation of a function
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+    public function getFunctionAnnotation(\ReflectionFunction $property, $annotationName);

+ 101 - 0
vendor/doctrine/annotations/docs/en/index.rst

@@ -0,0 +1,101 @@
+Introduction
+============
+
+Doctrine Annotations allows to implement custom annotation
+functionality for PHP classes and functions.
+
+.. code-block:: php
+
+    class Foo
+    {
+        /**
+         * @MyAnnotation(myProperty="value")
+         */
+        private $bar;
+    }
+
+Annotations aren't implemented in PHP itself which is why this component
+offers a way to use the PHP doc-blocks as a place for the well known
+annotation syntax using the ``@`` char.
+
+Annotations in Doctrine are used for the ORM configuration to build the
+class mapping, but it can be used in other projects for other purposes
+too.
+
+Installation
+============
+
+You can install the Annotation component with composer:
+
+.. code-block::
+
+    $ composer require doctrine/annotations
+
+Create an annotation class
+==========================
+
+An annotation class is a representation of the later used annotation
+configuration in classes. The annotation class of the previous example
+looks like this:
+
+.. code-block:: php
+
+    /**
+     * @Annotation
+     */
+    final class MyAnnotation
+    {
+        public $myProperty;
+    }
+
+The annotation class is declared as an annotation by ``@Annotation``.
+
+:ref:`Read more about custom annotations. <custom>`
+
+Reading annotations
+===================
+
+The access to the annotations happens by reflection of the class or function
+containing them. There are multiple reader-classes implementing the
+``Doctrine\Common\Annotations\Reader`` interface, that can access the
+annotations of a class. A common one is
+``Doctrine\Common\Annotations\AnnotationReader``:
+
+.. code-block:: php
+
+    use Doctrine\Common\Annotations\AnnotationReader;
+    use Doctrine\Common\Annotations\AnnotationRegistry;
+
+    // Deprecated and will be removed in 2.0 but currently needed
+    AnnotationRegistry::registerLoader('class_exists');
+
+    $reflectionClass = new ReflectionClass(Foo::class);
+    $property = $reflectionClass->getProperty('bar');
+
+    $reader = new AnnotationReader();
+    $myAnnotation = $reader->getPropertyAnnotation(
+        $property,
+        MyAnnotation::class
+    );
+
+    echo $myAnnotation->myProperty; // result: "value"
+
+Note that ``AnnotationRegistry::registerLoader('class_exists')`` only works
+if you already have an autoloader configured (i.e. composer autoloader).
+Otherwise, :ref:`please take a look to the other annotation autoload mechanisms <annotations>`.
+
+A reader has multiple methods to access the annotations of a class or
+function.
+
+:ref:`Read more about handling annotations. <annotations>`
+
+IDE Support
+-----------
+
+Some IDEs already provide support for annotations:
+
+- Eclipse via the `Symfony2 Plugin <http://symfony.dubture.com/>`_
+- PhpStorm via the `PHP Annotations Plugin <https://plugins.jetbrains.com/plugin/7320-php-annotations>`_ or the `Symfony Plugin <https://plugins.jetbrains.com/plugin/7219-symfony-support>`_
+
+.. _Read more about handling annotations.: annotations
+.. _Read more about custom annotations.: custom

+ 6 - 0
vendor/doctrine/annotations/docs/en/sidebar.rst

@@ -0,0 +1,6 @@
+.. toctree::
+    :depth: 3
+
+    index
+    annotations
+    custom

+ 59 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use BadMethodCallException;
+
+use function sprintf;
+
+/**
+ * Annotations class.
+ */
+class Annotation
+{
+    /**
+     * Value property. Common among all derived classes.
+     *
+     * @var mixed
+     */
+    public $value;
+
+    /**
+     * @param array<string, mixed> $data Key-value for properties to be defined in this class.
+     */
+    final public function __construct(array $data)
+    {
+        foreach ($data as $key => $value) {
+            $this->$key = $value;
+        }
+    }
+
+    /**
+     * Error handler for unknown property accessor in Annotation class.
+     *
+     * @param string $name Unknown property name.
+     *
+     * @throws BadMethodCallException
+     */
+    public function __get($name)
+    {
+        throw new BadMethodCallException(
+            sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class)
+        );
+    }
+
+    /**
+     * Error handler for unknown property mutator in Annotation class.
+     *
+     * @param string $name  Unknown property name.
+     * @param mixed  $value Property value.
+     *
+     * @throws BadMethodCallException
+     */
+    public function __set($name, $value)
+    {
+        throw new BadMethodCallException(
+            sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class)
+        );
+    }
+}

+ 21 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace Doctrine\Common\Annotations\Annotation;
+
+/**
+ * Annotation that can be used to signal to the parser
+ * to check the attribute type during the parsing process.
+ *
+ * @Annotation
+ */
+final class Attribute
+{
+    /** @var string */
+    public $name;
+
+    /** @var string */
+    public $type;
+
+    /** @var bool */
+    public $required = false;
+}

+ 15 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace Doctrine\Common\Annotations\Annotation;
+
+/**
+ * Annotation that can be used to signal to the parser
+ * to check the types of all declared attributes during the parsing process.
+ *
+ * @Annotation
+ */
+final class Attributes
+{
+    /** @var array<Attribute> */
+    public $value;
+}

+ 69 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace Doctrine\Common\Annotations\Annotation;
+
+use InvalidArgumentException;
+
+use function get_class;
+use function gettype;
+use function in_array;
+use function is_object;
+use function is_scalar;
+use function sprintf;
+
+/**
+ * Annotation that can be used to signal to the parser
+ * to check the available values during the parsing process.
+ *
+ * @Annotation
+ * @Attributes({
+ *    @Attribute("value",   required = true,  type = "array"),
+ *    @Attribute("literal", required = false, type = "array")
+ * })
+ */
+final class Enum
+{
+    /** @phpstan-var list<scalar> */
+    public $value;
+
+    /**
+     * Literal target declaration.
+     *
+     * @var mixed[]
+     */
+    public $literal;
+
+    /**
+     * @throws InvalidArgumentException
+     *
+     * @phpstan-param array{literal?: mixed[], value: list<scalar>} $values
+     */
+    public function __construct(array $values)
+    {
+        if (! isset($values['literal'])) {
+            $values['literal'] = [];
+        }
+
+        foreach ($values['value'] as $var) {
+            if (! is_scalar($var)) {
+                throw new InvalidArgumentException(sprintf(
+                    '@Enum supports only scalar values "%s" given.',
+                    is_object($var) ? get_class($var) : gettype($var)
+                ));
+            }
+        }
+
+        foreach ($values['literal'] as $key => $var) {
+            if (! in_array($key, $values['value'])) {
+                throw new InvalidArgumentException(sprintf(
+                    'Undefined enumerator value "%s" for literal "%s".',
+                    $key,
+                    $var
+                ));
+            }
+        }
+
+        $this->value   = $values['value'];
+        $this->literal = $values['literal'];
+    }
+}

+ 43 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace Doctrine\Common\Annotations\Annotation;
+
+use RuntimeException;
+
+use function is_array;
+use function is_string;
+use function json_encode;
+use function sprintf;
+
+/**
+ * Annotation that can be used to signal to the parser to ignore specific
+ * annotations during the parsing process.
+ *
+ * @Annotation
+ */
+final class IgnoreAnnotation
+{
+    /** @phpstan-var list<string> */
+    public $names;
+
+    /**
+     * @throws RuntimeException
+     *
+     * @phpstan-param array{value: string|list<string>} $values
+     */
+    public function __construct(array $values)
+    {
+        if (is_string($values['value'])) {
+            $values['value'] = [$values['value']];
+        }
+
+        if (! is_array($values['value'])) {
+            throw new RuntimeException(sprintf(
+                '@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.',
+                json_encode($values['value'])
+            ));
+        }
+
+        $this->names = $values['value'];
+    }
+}

+ 13 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/NamedArgumentConstructor.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace Doctrine\Common\Annotations\Annotation;
+
+/**
+ * Annotation that indicates that the annotated class should be constructed with a named argument call.
+ *
+ * @Annotation
+ * @Target("CLASS")
+ */
+final class NamedArgumentConstructor
+{
+}

+ 13 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace Doctrine\Common\Annotations\Annotation;
+
+/**
+ * Annotation that can be used to signal to the parser
+ * to check if that attribute is required during the parsing process.
+ *
+ * @Annotation
+ */
+final class Required
+{
+}

+ 101 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php

@@ -0,0 +1,101 @@
+<?php
+
+namespace Doctrine\Common\Annotations\Annotation;
+
+use InvalidArgumentException;
+
+use function array_keys;
+use function get_class;
+use function gettype;
+use function implode;
+use function is_array;
+use function is_object;
+use function is_string;
+use function sprintf;
+
+/**
+ * Annotation that can be used to signal to the parser
+ * to check the annotation target during the parsing process.
+ *
+ * @Annotation
+ */
+final class Target
+{
+    public const TARGET_CLASS      = 1;
+    public const TARGET_METHOD     = 2;
+    public const TARGET_PROPERTY   = 4;
+    public const TARGET_ANNOTATION = 8;
+    public const TARGET_FUNCTION   = 16;
+    public const TARGET_ALL        = 31;
+
+    /** @var array<string, int> */
+    private static $map = [
+        'ALL'        => self::TARGET_ALL,
+        'CLASS'      => self::TARGET_CLASS,
+        'METHOD'     => self::TARGET_METHOD,
+        'PROPERTY'   => self::TARGET_PROPERTY,
+        'FUNCTION'   => self::TARGET_FUNCTION,
+        'ANNOTATION' => self::TARGET_ANNOTATION,
+    ];
+
+    /** @phpstan-var list<string> */
+    public $value;
+
+    /**
+     * Targets as bitmask.
+     *
+     * @var int
+     */
+    public $targets;
+
+    /**
+     * Literal target declaration.
+     *
+     * @var string
+     */
+    public $literal;
+
+    /**
+     * @throws InvalidArgumentException
+     *
+     * @phpstan-param array{value?: string|list<string>} $values
+     */
+    public function __construct(array $values)
+    {
+        if (! isset($values['value'])) {
+            $values['value'] = null;
+        }
+
+        if (is_string($values['value'])) {
+            $values['value'] = [$values['value']];
+        }
+
+        if (! is_array($values['value'])) {
+            throw new InvalidArgumentException(
+                sprintf(
+                    '@Target expects either a string value, or an array of strings, "%s" given.',
+                    is_object($values['value']) ? get_class($values['value']) : gettype($values['value'])
+                )
+            );
+        }
+
+        $bitmask = 0;
+        foreach ($values['value'] as $literal) {
+            if (! isset(self::$map[$literal])) {
+                throw new InvalidArgumentException(
+                    sprintf(
+                        'Invalid Target "%s". Available targets: [%s]',
+                        $literal,
+                        implode(', ', array_keys(self::$map))
+                    )
+                );
+            }
+
+            $bitmask |= self::$map[$literal];
+        }
+
+        $this->targets = $bitmask;
+        $this->value   = $values['value'];
+        $this->literal = implode(', ', $this->value);
+    }
+}

+ 171 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php

@@ -0,0 +1,171 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use Exception;
+
+use function get_class;
+use function gettype;
+use function implode;
+use function is_object;
+use function sprintf;
+
+/**
+ * Description of AnnotationException
+ */
+class AnnotationException extends Exception
+{
+    /**
+     * Creates a new AnnotationException describing a Syntax error.
+     *
+     * @param string $message Exception message
+     *
+     * @return AnnotationException
+     */
+    public static function syntaxError($message)
+    {
+        return new self('[Syntax Error] ' . $message);
+    }
+
+    /**
+     * Creates a new AnnotationException describing a Semantical error.
+     *
+     * @param string $message Exception message
+     *
+     * @return AnnotationException
+     */
+    public static function semanticalError($message)
+    {
+        return new self('[Semantical Error] ' . $message);
+    }
+
+    /**
+     * Creates a new AnnotationException describing an error which occurred during
+     * the creation of the annotation.
+     *
+     * @param string $message
+     *
+     * @return AnnotationException
+     */
+    public static function creationError($message)
+    {
+        return new self('[Creation Error] ' . $message);
+    }
+
+    /**
+     * Creates a new AnnotationException describing a type error.
+     *
+     * @param string $message
+     *
+     * @return AnnotationException
+     */
+    public static function typeError($message)
+    {
+        return new self('[Type Error] ' . $message);
+    }
+
+    /**
+     * Creates a new AnnotationException describing a constant semantical error.
+     *
+     * @param string $identifier
+     * @param string $context
+     *
+     * @return AnnotationException
+     */
+    public static function semanticalErrorConstants($identifier, $context = null)
+    {
+        return self::semanticalError(sprintf(
+            "Couldn't find constant %s%s.",
+            $identifier,
+            $context ? ', ' . $context : ''
+        ));
+    }
+
+    /**
+     * Creates a new AnnotationException describing an type error of an attribute.
+     *
+     * @param string $attributeName
+     * @param string $annotationName
+     * @param string $context
+     * @param string $expected
+     * @param mixed  $actual
+     *
+     * @return AnnotationException
+     */
+    public static function attributeTypeError($attributeName, $annotationName, $context, $expected, $actual)
+    {
+        return self::typeError(sprintf(
+            'Attribute "%s" of @%s declared on %s expects %s, but got %s.',
+            $attributeName,
+            $annotationName,
+            $context,
+            $expected,
+            is_object($actual) ? 'an instance of ' . get_class($actual) : gettype($actual)
+        ));
+    }
+
+    /**
+     * Creates a new AnnotationException describing an required error of an attribute.
+     *
+     * @param string $attributeName
+     * @param string $annotationName
+     * @param string $context
+     * @param string $expected
+     *
+     * @return AnnotationException
+     */
+    public static function requiredError($attributeName, $annotationName, $context, $expected)
+    {
+        return self::typeError(sprintf(
+            'Attribute "%s" of @%s declared on %s expects %s. This value should not be null.',
+            $attributeName,
+            $annotationName,
+            $context,
+            $expected
+        ));
+    }
+
+    /**
+     * Creates a new AnnotationException describing a invalid enummerator.
+     *
+     * @param string $attributeName
+     * @param string $annotationName
+     * @param string $context
+     * @param mixed  $given
+     *
+     * @return AnnotationException
+     *
+     * @phpstan-param list<string>        $available
+     */
+    public static function enumeratorError($attributeName, $annotationName, $context, $available, $given)
+    {
+        return new self(sprintf(
+            '[Enum Error] Attribute "%s" of @%s declared on %s accepts only [%s], but got %s.',
+            $attributeName,
+            $annotationName,
+            $context,
+            implode(', ', $available),
+            is_object($given) ? get_class($given) : $given
+        ));
+    }
+
+    /**
+     * @return AnnotationException
+     */
+    public static function optimizerPlusSaveComments()
+    {
+        return new self(
+            'You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1.'
+        );
+    }
+
+    /**
+     * @return AnnotationException
+     */
+    public static function optimizerPlusLoadComments()
+    {
+        return new self(
+            'You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1.'
+        );
+    }
+}

+ 389 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php

@@ -0,0 +1,389 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
+use Doctrine\Common\Annotations\Annotation\Target;
+use ReflectionClass;
+use ReflectionFunction;
+use ReflectionMethod;
+use ReflectionProperty;
+
+use function array_merge;
+use function class_exists;
+use function extension_loaded;
+use function ini_get;
+
+/**
+ * A reader for docblock annotations.
+ */
+class AnnotationReader implements Reader
+{
+    /**
+     * Global map for imports.
+     *
+     * @var array<string, class-string>
+     */
+    private static $globalImports = [
+        'ignoreannotation' => Annotation\IgnoreAnnotation::class,
+    ];
+
+    /**
+     * A list with annotations that are not causing exceptions when not resolved to an annotation class.
+     *
+     * The names are case sensitive.
+     *
+     * @var array<string, true>
+     */
+    private static $globalIgnoredNames = ImplicitlyIgnoredAnnotationNames::LIST;
+
+    /**
+     * A list with annotations that are not causing exceptions when not resolved to an annotation class.
+     *
+     * The names are case sensitive.
+     *
+     * @var array<string, true>
+     */
+    private static $globalIgnoredNamespaces = [];
+
+    /**
+     * Add a new annotation to the globally ignored annotation names with regard to exception handling.
+     *
+     * @param string $name
+     */
+    public static function addGlobalIgnoredName($name)
+    {
+        self::$globalIgnoredNames[$name] = true;
+    }
+
+    /**
+     * Add a new annotation to the globally ignored annotation namespaces with regard to exception handling.
+     *
+     * @param string $namespace
+     */
+    public static function addGlobalIgnoredNamespace($namespace)
+    {
+        self::$globalIgnoredNamespaces[$namespace] = true;
+    }
+
+    /**
+     * Annotations parser.
+     *
+     * @var DocParser
+     */
+    private $parser;
+
+    /**
+     * Annotations parser used to collect parsing metadata.
+     *
+     * @var DocParser
+     */
+    private $preParser;
+
+    /**
+     * PHP parser used to collect imports.
+     *
+     * @var PhpParser
+     */
+    private $phpParser;
+
+    /**
+     * In-memory cache mechanism to store imported annotations per class.
+     *
+     * @psalm-var array<'class'|'function', array<string, array<string, class-string>>>
+     */
+    private $imports = [];
+
+    /**
+     * In-memory cache mechanism to store ignored annotations per class.
+     *
+     * @psalm-var array<'class'|'function', array<string, array<string, true>>>
+     */
+    private $ignoredAnnotationNames = [];
+
+    /**
+     * Initializes a new AnnotationReader.
+     *
+     * @throws AnnotationException
+     */
+    public function __construct(?DocParser $parser = null)
+    {
+        if (
+            extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === '0' ||
+            ini_get('opcache.save_comments') === '0')
+        ) {
+            throw AnnotationException::optimizerPlusSaveComments();
+        }
+
+        if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') === 0) {
+            throw AnnotationException::optimizerPlusSaveComments();
+        }
+
+        // Make sure that the IgnoreAnnotation annotation is loaded
+        class_exists(IgnoreAnnotation::class);
+
+        $this->parser = $parser ?: new DocParser();
+
+        $this->preParser = new DocParser();
+
+        $this->preParser->setImports(self::$globalImports);
+        $this->preParser->setIgnoreNotImportedAnnotations(true);
+        $this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames);
+
+        $this->phpParser = new PhpParser();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getClassAnnotations(ReflectionClass $class)
+    {
+        $this->parser->setTarget(Target::TARGET_CLASS);
+        $this->parser->setImports($this->getImports($class));
+        $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
+        $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
+
+        return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getClassAnnotation(ReflectionClass $class, $annotationName)
+    {
+        $annotations = $this->getClassAnnotations($class);
+
+        foreach ($annotations as $annotation) {
+            if ($annotation instanceof $annotationName) {
+                return $annotation;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPropertyAnnotations(ReflectionProperty $property)
+    {
+        $class   = $property->getDeclaringClass();
+        $context = 'property ' . $class->getName() . '::$' . $property->getName();
+
+        $this->parser->setTarget(Target::TARGET_PROPERTY);
+        $this->parser->setImports($this->getPropertyImports($property));
+        $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
+        $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
+
+        return $this->parser->parse($property->getDocComment(), $context);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
+    {
+        $annotations = $this->getPropertyAnnotations($property);
+
+        foreach ($annotations as $annotation) {
+            if ($annotation instanceof $annotationName) {
+                return $annotation;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethodAnnotations(ReflectionMethod $method)
+    {
+        $class   = $method->getDeclaringClass();
+        $context = 'method ' . $class->getName() . '::' . $method->getName() . '()';
+
+        $this->parser->setTarget(Target::TARGET_METHOD);
+        $this->parser->setImports($this->getMethodImports($method));
+        $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
+        $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
+
+        return $this->parser->parse($method->getDocComment(), $context);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
+    {
+        $annotations = $this->getMethodAnnotations($method);
+
+        foreach ($annotations as $annotation) {
+            if ($annotation instanceof $annotationName) {
+                return $annotation;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Gets the annotations applied to a function.
+     *
+     * @phpstan-return list<object> An array of Annotations.
+     */
+    public function getFunctionAnnotations(ReflectionFunction $function): array
+    {
+        $context = 'function ' . $function->getName();
+
+        $this->parser->setTarget(Target::TARGET_FUNCTION);
+        $this->parser->setImports($this->getImports($function));
+        $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($function));
+        $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
+
+        return $this->parser->parse($function->getDocComment(), $context);
+    }
+
+    /**
+     * Gets a function annotation.
+     *
+     * @return object|null The Annotation or NULL, if the requested annotation does not exist.
+     */
+    public function getFunctionAnnotation(ReflectionFunction $function, string $annotationName)
+    {
+        $annotations = $this->getFunctionAnnotations($function);
+
+        foreach ($annotations as $annotation) {
+            if ($annotation instanceof $annotationName) {
+                return $annotation;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the ignored annotations for the given class or function.
+     *
+     * @param ReflectionClass|ReflectionFunction $reflection
+     *
+     * @return array<string, true>
+     */
+    private function getIgnoredAnnotationNames($reflection): array
+    {
+        $type = $reflection instanceof ReflectionClass ? 'class' : 'function';
+        $name = $reflection->getName();
+
+        if (isset($this->ignoredAnnotationNames[$type][$name])) {
+            return $this->ignoredAnnotationNames[$type][$name];
+        }
+
+        $this->collectParsingMetadata($reflection);
+
+        return $this->ignoredAnnotationNames[$type][$name];
+    }
+
+    /**
+     * Retrieves imports for a class or a function.
+     *
+     * @param ReflectionClass|ReflectionFunction $reflection
+     *
+     * @return array<string, class-string>
+     */
+    private function getImports($reflection): array
+    {
+        $type = $reflection instanceof ReflectionClass ? 'class' : 'function';
+        $name = $reflection->getName();
+
+        if (isset($this->imports[$type][$name])) {
+            return $this->imports[$type][$name];
+        }
+
+        $this->collectParsingMetadata($reflection);
+
+        return $this->imports[$type][$name];
+    }
+
+    /**
+     * Retrieves imports for methods.
+     *
+     * @return array<string, class-string>
+     */
+    private function getMethodImports(ReflectionMethod $method)
+    {
+        $class        = $method->getDeclaringClass();
+        $classImports = $this->getImports($class);
+
+        $traitImports = [];
+
+        foreach ($class->getTraits() as $trait) {
+            if (
+                ! $trait->hasMethod($method->getName())
+                || $trait->getFileName() !== $method->getFileName()
+            ) {
+                continue;
+            }
+
+            $traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait));
+        }
+
+        return array_merge($classImports, $traitImports);
+    }
+
+    /**
+     * Retrieves imports for properties.
+     *
+     * @return array<string, class-string>
+     */
+    private function getPropertyImports(ReflectionProperty $property)
+    {
+        $class        = $property->getDeclaringClass();
+        $classImports = $this->getImports($class);
+
+        $traitImports = [];
+
+        foreach ($class->getTraits() as $trait) {
+            if (! $trait->hasProperty($property->getName())) {
+                continue;
+            }
+
+            $traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait));
+        }
+
+        return array_merge($classImports, $traitImports);
+    }
+
+    /**
+     * Collects parsing metadata for a given class or function.
+     *
+     * @param ReflectionClass|ReflectionFunction $reflection
+     */
+    private function collectParsingMetadata($reflection): void
+    {
+        $type = $reflection instanceof ReflectionClass ? 'class' : 'function';
+        $name = $reflection->getName();
+
+        $ignoredAnnotationNames = self::$globalIgnoredNames;
+        $annotations            = $this->preParser->parse($reflection->getDocComment(), $type . ' ' . $name);
+
+        foreach ($annotations as $annotation) {
+            if (! ($annotation instanceof IgnoreAnnotation)) {
+                continue;
+            }
+
+            foreach ($annotation->names as $annot) {
+                $ignoredAnnotationNames[$annot] = true;
+            }
+        }
+
+        $this->imports[$type][$name] = array_merge(
+            self::$globalImports,
+            $this->phpParser->parseUseStatements($reflection),
+            [
+                '__NAMESPACE__' => $reflection->getNamespaceName(),
+                'self' => $name,
+            ]
+        );
+
+        $this->ignoredAnnotationNames[$type][$name] = $ignoredAnnotationNames;
+    }
+}

+ 190 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php

@@ -0,0 +1,190 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use function array_key_exists;
+use function array_merge;
+use function class_exists;
+use function in_array;
+use function is_file;
+use function str_replace;
+use function stream_resolve_include_path;
+use function strpos;
+
+use const DIRECTORY_SEPARATOR;
+
+final class AnnotationRegistry
+{
+    /**
+     * A map of namespaces to use for autoloading purposes based on a PSR-0 convention.
+     *
+     * Contains the namespace as key and an array of directories as value. If the value is NULL
+     * the include path is used for checking for the corresponding file.
+     *
+     * This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own.
+     *
+     * @var string[][]|string[]|null[]
+     */
+    private static $autoloadNamespaces = [];
+
+    /**
+     * A map of autoloader callables.
+     *
+     * @var callable[]
+     */
+    private static $loaders = [];
+
+    /**
+     * An array of classes which cannot be found
+     *
+     * @var null[] indexed by class name
+     */
+    private static $failedToAutoload = [];
+
+    /**
+     * Whenever registerFile() was used. Disables use of standard autoloader.
+     *
+     * @var bool
+     */
+    private static $registerFileUsed = false;
+
+    public static function reset(): void
+    {
+        self::$autoloadNamespaces = [];
+        self::$loaders            = [];
+        self::$failedToAutoload   = [];
+        self::$registerFileUsed   = false;
+    }
+
+    /**
+     * Registers file.
+     *
+     * @deprecated This method is deprecated and will be removed in
+     *             doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
+     */
+    public static function registerFile(string $file): void
+    {
+        self::$registerFileUsed = true;
+
+        require_once $file;
+    }
+
+    /**
+     * Adds a namespace with one or many directories to look for files or null for the include path.
+     *
+     * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
+     *
+     * @deprecated This method is deprecated and will be removed in
+     *             doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
+     *
+     * @phpstan-param string|list<string>|null $dirs
+     */
+    public static function registerAutoloadNamespace(string $namespace, $dirs = null): void
+    {
+        self::$autoloadNamespaces[$namespace] = $dirs;
+    }
+
+    /**
+     * Registers multiple namespaces.
+     *
+     * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
+     *
+     * @deprecated This method is deprecated and will be removed in
+     *             doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
+     *
+     * @param string[][]|string[]|null[] $namespaces indexed by namespace name
+     */
+    public static function registerAutoloadNamespaces(array $namespaces): void
+    {
+        self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces);
+    }
+
+    /**
+     * Registers an autoloading callable for annotations, much like spl_autoload_register().
+     *
+     * NOTE: These class loaders HAVE to be silent when a class was not found!
+     * IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class.
+     *
+     * @deprecated This method is deprecated and will be removed in
+     *             doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
+     */
+    public static function registerLoader(callable $callable): void
+    {
+        // Reset our static cache now that we have a new loader to work with
+        self::$failedToAutoload = [];
+        self::$loaders[]        = $callable;
+    }
+
+    /**
+     * Registers an autoloading callable for annotations, if it is not already registered
+     *
+     * @deprecated This method is deprecated and will be removed in
+     *             doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
+     */
+    public static function registerUniqueLoader(callable $callable): void
+    {
+        if (in_array($callable, self::$loaders, true)) {
+            return;
+        }
+
+        self::registerLoader($callable);
+    }
+
+    /**
+     * Autoloads an annotation class silently.
+     */
+    public static function loadAnnotationClass(string $class): bool
+    {
+        if (class_exists($class, false)) {
+            return true;
+        }
+
+        if (array_key_exists($class, self::$failedToAutoload)) {
+            return false;
+        }
+
+        foreach (self::$autoloadNamespaces as $namespace => $dirs) {
+            if (strpos($class, $namespace) !== 0) {
+                continue;
+            }
+
+            $file = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
+
+            if ($dirs === null) {
+                $path = stream_resolve_include_path($file);
+                if ($path) {
+                    require $path;
+
+                    return true;
+                }
+            } else {
+                foreach ((array) $dirs as $dir) {
+                    if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
+                        require $dir . DIRECTORY_SEPARATOR . $file;
+
+                        return true;
+                    }
+                }
+            }
+        }
+
+        foreach (self::$loaders as $loader) {
+            if ($loader($class) === true) {
+                return true;
+            }
+        }
+
+        if (
+            self::$loaders === [] &&
+            self::$autoloadNamespaces === [] &&
+            self::$registerFileUsed === false &&
+            class_exists($class)
+        ) {
+            return true;
+        }
+
+        self::$failedToAutoload[$class] = null;
+
+        return false;
+    }
+}

+ 264 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php

@@ -0,0 +1,264 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use Doctrine\Common\Cache\Cache;
+use ReflectionClass;
+use ReflectionMethod;
+use ReflectionProperty;
+
+use function array_map;
+use function array_merge;
+use function assert;
+use function filemtime;
+use function max;
+use function time;
+
+/**
+ * A cache aware annotation reader.
+ */
+final class CachedReader implements Reader
+{
+    /** @var Reader */
+    private $delegate;
+
+    /** @var Cache */
+    private $cache;
+
+    /** @var bool */
+    private $debug;
+
+    /** @var array<string, array<object>> */
+    private $loadedAnnotations = [];
+
+    /** @var int[] */
+    private $loadedFilemtimes = [];
+
+    /**
+     * @param bool $debug
+     */
+    public function __construct(Reader $reader, Cache $cache, $debug = false)
+    {
+        $this->delegate = $reader;
+        $this->cache    = $cache;
+        $this->debug    = (bool) $debug;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getClassAnnotations(ReflectionClass $class)
+    {
+        $cacheKey = $class->getName();
+
+        if (isset($this->loadedAnnotations[$cacheKey])) {
+            return $this->loadedAnnotations[$cacheKey];
+        }
+
+        $annots = $this->fetchFromCache($cacheKey, $class);
+        if ($annots === false) {
+            $annots = $this->delegate->getClassAnnotations($class);
+            $this->saveToCache($cacheKey, $annots);
+        }
+
+        return $this->loadedAnnotations[$cacheKey] = $annots;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getClassAnnotation(ReflectionClass $class, $annotationName)
+    {
+        foreach ($this->getClassAnnotations($class) as $annot) {
+            if ($annot instanceof $annotationName) {
+                return $annot;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPropertyAnnotations(ReflectionProperty $property)
+    {
+        $class    = $property->getDeclaringClass();
+        $cacheKey = $class->getName() . '$' . $property->getName();
+
+        if (isset($this->loadedAnnotations[$cacheKey])) {
+            return $this->loadedAnnotations[$cacheKey];
+        }
+
+        $annots = $this->fetchFromCache($cacheKey, $class);
+        if ($annots === false) {
+            $annots = $this->delegate->getPropertyAnnotations($property);
+            $this->saveToCache($cacheKey, $annots);
+        }
+
+        return $this->loadedAnnotations[$cacheKey] = $annots;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
+    {
+        foreach ($this->getPropertyAnnotations($property) as $annot) {
+            if ($annot instanceof $annotationName) {
+                return $annot;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethodAnnotations(ReflectionMethod $method)
+    {
+        $class    = $method->getDeclaringClass();
+        $cacheKey = $class->getName() . '#' . $method->getName();
+
+        if (isset($this->loadedAnnotations[$cacheKey])) {
+            return $this->loadedAnnotations[$cacheKey];
+        }
+
+        $annots = $this->fetchFromCache($cacheKey, $class);
+        if ($annots === false) {
+            $annots = $this->delegate->getMethodAnnotations($method);
+            $this->saveToCache($cacheKey, $annots);
+        }
+
+        return $this->loadedAnnotations[$cacheKey] = $annots;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
+    {
+        foreach ($this->getMethodAnnotations($method) as $annot) {
+            if ($annot instanceof $annotationName) {
+                return $annot;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Clears loaded annotations.
+     *
+     * @return void
+     */
+    public function clearLoadedAnnotations()
+    {
+        $this->loadedAnnotations = [];
+        $this->loadedFilemtimes  = [];
+    }
+
+    /**
+     * Fetches a value from the cache.
+     *
+     * @param string $cacheKey The cache key.
+     *
+     * @return mixed The cached value or false when the value is not in cache.
+     */
+    private function fetchFromCache($cacheKey, ReflectionClass $class)
+    {
+        $data = $this->cache->fetch($cacheKey);
+        if ($data !== false) {
+            if (! $this->debug || $this->isCacheFresh($cacheKey, $class)) {
+                return $data;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Saves a value to the cache.
+     *
+     * @param string $cacheKey The cache key.
+     * @param mixed  $value    The value.
+     *
+     * @return void
+     */
+    private function saveToCache($cacheKey, $value)
+    {
+        $this->cache->save($cacheKey, $value);
+        if (! $this->debug) {
+            return;
+        }
+
+        $this->cache->save('[C]' . $cacheKey, time());
+    }
+
+    /**
+     * Checks if the cache is fresh.
+     *
+     * @param string $cacheKey
+     *
+     * @return bool
+     */
+    private function isCacheFresh($cacheKey, ReflectionClass $class)
+    {
+        $lastModification = $this->getLastModification($class);
+        if ($lastModification === 0) {
+            return true;
+        }
+
+        return $this->cache->fetch('[C]' . $cacheKey) >= $lastModification;
+    }
+
+    /**
+     * Returns the time the class was last modified, testing traits and parents
+     */
+    private function getLastModification(ReflectionClass $class): int
+    {
+        $filename = $class->getFileName();
+
+        if (isset($this->loadedFilemtimes[$filename])) {
+            return $this->loadedFilemtimes[$filename];
+        }
+
+        $parent = $class->getParentClass();
+
+        $lastModification =  max(array_merge(
+            [$filename ? filemtime($filename) : 0],
+            array_map(function (ReflectionClass $reflectionTrait): int {
+                return $this->getTraitLastModificationTime($reflectionTrait);
+            }, $class->getTraits()),
+            array_map(function (ReflectionClass $class): int {
+                return $this->getLastModification($class);
+            }, $class->getInterfaces()),
+            $parent ? [$this->getLastModification($parent)] : []
+        ));
+
+        assert($lastModification !== false);
+
+        return $this->loadedFilemtimes[$filename] = $lastModification;
+    }
+
+    private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int
+    {
+        $fileName = $reflectionTrait->getFileName();
+
+        if (isset($this->loadedFilemtimes[$fileName])) {
+            return $this->loadedFilemtimes[$fileName];
+        }
+
+        $lastModificationTime = max(array_merge(
+            [$fileName ? filemtime($fileName) : 0],
+            array_map(function (ReflectionClass $reflectionTrait): int {
+                return $this->getTraitLastModificationTime($reflectionTrait);
+            }, $reflectionTrait->getTraits())
+        ));
+
+        assert($lastModificationTime !== false);
+
+        return $this->loadedFilemtimes[$fileName] = $lastModificationTime;
+    }
+}

+ 129 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php

@@ -0,0 +1,129 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use Doctrine\Common\Lexer\AbstractLexer;
+
+use function ctype_alpha;
+use function is_numeric;
+use function str_replace;
+use function stripos;
+use function strlen;
+use function strpos;
+use function strtolower;
+use function substr;
+
+/**
+ * Simple lexer for docblock annotations.
+ */
+final class DocLexer extends AbstractLexer
+{
+    public const T_NONE    = 1;
+    public const T_INTEGER = 2;
+    public const T_STRING  = 3;
+    public const T_FLOAT   = 4;
+
+    // All tokens that are also identifiers should be >= 100
+    public const T_IDENTIFIER          = 100;
+    public const T_AT                  = 101;
+    public const T_CLOSE_CURLY_BRACES  = 102;
+    public const T_CLOSE_PARENTHESIS   = 103;
+    public const T_COMMA               = 104;
+    public const T_EQUALS              = 105;
+    public const T_FALSE               = 106;
+    public const T_NAMESPACE_SEPARATOR = 107;
+    public const T_OPEN_CURLY_BRACES   = 108;
+    public const T_OPEN_PARENTHESIS    = 109;
+    public const T_TRUE                = 110;
+    public const T_NULL                = 111;
+    public const T_COLON               = 112;
+    public const T_MINUS               = 113;
+
+    /** @var array<string, int> */
+    protected $noCase = [
+        '@'  => self::T_AT,
+        ','  => self::T_COMMA,
+        '('  => self::T_OPEN_PARENTHESIS,
+        ')'  => self::T_CLOSE_PARENTHESIS,
+        '{'  => self::T_OPEN_CURLY_BRACES,
+        '}'  => self::T_CLOSE_CURLY_BRACES,
+        '='  => self::T_EQUALS,
+        ':'  => self::T_COLON,
+        '-'  => self::T_MINUS,
+        '\\' => self::T_NAMESPACE_SEPARATOR,
+    ];
+
+    /** @var array<string, int> */
+    protected $withCase = [
+        'true'  => self::T_TRUE,
+        'false' => self::T_FALSE,
+        'null'  => self::T_NULL,
+    ];
+
+    /**
+     * Whether the next token starts immediately, or if there were
+     * non-captured symbols before that
+     */
+    public function nextTokenIsAdjacent(): bool
+    {
+        return $this->token === null
+            || ($this->lookahead !== null
+                && ($this->lookahead['position'] - $this->token['position']) === strlen($this->token['value']));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getCatchablePatterns()
+    {
+        return [
+            '[a-z_\\\][a-z0-9_\:\\\]*[a-z_][a-z0-9_]*',
+            '(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?',
+            '"(?:""|[^"])*+"',
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getNonCatchablePatterns()
+    {
+        return ['\s+', '\*+', '(.)'];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getType(&$value)
+    {
+        $type = self::T_NONE;
+
+        if ($value[0] === '"') {
+            $value = str_replace('""', '"', substr($value, 1, strlen($value) - 2));
+
+            return self::T_STRING;
+        }
+
+        if (isset($this->noCase[$value])) {
+            return $this->noCase[$value];
+        }
+
+        if ($value[0] === '_' || $value[0] === '\\' || ctype_alpha($value[0])) {
+            return self::T_IDENTIFIER;
+        }
+
+        $lowerValue = strtolower($value);
+
+        if (isset($this->withCase[$lowerValue])) {
+            return $this->withCase[$lowerValue];
+        }
+
+        // Checking numeric value
+        if (is_numeric($value)) {
+            return strpos($value, '.') !== false || stripos($value, 'e') !== false
+                ? self::T_FLOAT : self::T_INTEGER;
+        }
+
+        return $type;
+    }
+}

+ 1459 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php

@@ -0,0 +1,1459 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use Doctrine\Common\Annotations\Annotation\Attribute;
+use Doctrine\Common\Annotations\Annotation\Attributes;
+use Doctrine\Common\Annotations\Annotation\Enum;
+use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
+use Doctrine\Common\Annotations\Annotation\Target;
+use ReflectionClass;
+use ReflectionException;
+use ReflectionProperty;
+use RuntimeException;
+use stdClass;
+
+use function array_keys;
+use function array_map;
+use function array_pop;
+use function array_values;
+use function class_exists;
+use function constant;
+use function count;
+use function defined;
+use function explode;
+use function gettype;
+use function implode;
+use function in_array;
+use function interface_exists;
+use function is_array;
+use function is_object;
+use function json_encode;
+use function ltrim;
+use function preg_match;
+use function reset;
+use function rtrim;
+use function sprintf;
+use function stripos;
+use function strlen;
+use function strpos;
+use function strrpos;
+use function strtolower;
+use function substr;
+use function trim;
+
+use const PHP_VERSION_ID;
+
+/**
+ * A parser for docblock annotations.
+ *
+ * It is strongly discouraged to change the default annotation parsing process.
+ */
+final class DocParser
+{
+    /**
+     * An array of all valid tokens for a class name.
+     *
+     * @phpstan-var list<int>
+     */
+    private static $classIdentifiers = [
+        DocLexer::T_IDENTIFIER,
+        DocLexer::T_TRUE,
+        DocLexer::T_FALSE,
+        DocLexer::T_NULL,
+    ];
+
+    /**
+     * The lexer.
+     *
+     * @var DocLexer
+     */
+    private $lexer;
+
+    /**
+     * Current target context.
+     *
+     * @var int
+     */
+    private $target;
+
+    /**
+     * Doc parser used to collect annotation target.
+     *
+     * @var DocParser
+     */
+    private static $metadataParser;
+
+    /**
+     * Flag to control if the current annotation is nested or not.
+     *
+     * @var bool
+     */
+    private $isNestedAnnotation = false;
+
+    /**
+     * Hashmap containing all use-statements that are to be used when parsing
+     * the given doc block.
+     *
+     * @var array<string, class-string>
+     */
+    private $imports = [];
+
+    /**
+     * This hashmap is used internally to cache results of class_exists()
+     * look-ups.
+     *
+     * @var array<class-string, bool>
+     */
+    private $classExists = [];
+
+    /**
+     * Whether annotations that have not been imported should be ignored.
+     *
+     * @var bool
+     */
+    private $ignoreNotImportedAnnotations = false;
+
+    /**
+     * An array of default namespaces if operating in simple mode.
+     *
+     * @var string[]
+     */
+    private $namespaces = [];
+
+    /**
+     * A list with annotations that are not causing exceptions when not resolved to an annotation class.
+     *
+     * The names must be the raw names as used in the class, not the fully qualified
+     *
+     * @var bool[] indexed by annotation name
+     */
+    private $ignoredAnnotationNames = [];
+
+    /**
+     * A list with annotations in namespaced format
+     * that are not causing exceptions when not resolved to an annotation class.
+     *
+     * @var bool[] indexed by namespace name
+     */
+    private $ignoredAnnotationNamespaces = [];
+
+    /** @var string */
+    private $context = '';
+
+    /**
+     * Hash-map for caching annotation metadata.
+     *
+     * @var array<class-string, mixed[]>
+     */
+    private static $annotationMetadata = [
+        Annotation\Target::class => [
+            'is_annotation'                  => true,
+            'has_constructor'                => true,
+            'has_named_argument_constructor' => false,
+            'properties'                     => [],
+            'targets_literal'                => 'ANNOTATION_CLASS',
+            'targets'                        => Target::TARGET_CLASS,
+            'default_property'               => 'value',
+            'attribute_types'                => [
+                'value'  => [
+                    'required'   => false,
+                    'type'       => 'array',
+                    'array_type' => 'string',
+                    'value'      => 'array<string>',
+                ],
+            ],
+        ],
+        Annotation\Attribute::class => [
+            'is_annotation'                  => true,
+            'has_constructor'                => false,
+            'has_named_argument_constructor' => false,
+            'targets_literal'                => 'ANNOTATION_ANNOTATION',
+            'targets'                        => Target::TARGET_ANNOTATION,
+            'default_property'               => 'name',
+            'properties'                     => [
+                'name'      => 'name',
+                'type'      => 'type',
+                'required'  => 'required',
+            ],
+            'attribute_types'                => [
+                'value'  => [
+                    'required'  => true,
+                    'type'      => 'string',
+                    'value'     => 'string',
+                ],
+                'type'  => [
+                    'required'  => true,
+                    'type'      => 'string',
+                    'value'     => 'string',
+                ],
+                'required'  => [
+                    'required'  => false,
+                    'type'      => 'boolean',
+                    'value'     => 'boolean',
+                ],
+            ],
+        ],
+        Annotation\Attributes::class => [
+            'is_annotation'                  => true,
+            'has_constructor'                => false,
+            'has_named_argument_constructor' => false,
+            'targets_literal'                => 'ANNOTATION_CLASS',
+            'targets'                        => Target::TARGET_CLASS,
+            'default_property'               => 'value',
+            'properties'                     => ['value' => 'value'],
+            'attribute_types'                => [
+                'value' => [
+                    'type'      => 'array',
+                    'required'  => true,
+                    'array_type' => Annotation\Attribute::class,
+                    'value'     => 'array<' . Annotation\Attribute::class . '>',
+                ],
+            ],
+        ],
+        Annotation\Enum::class => [
+            'is_annotation'                  => true,
+            'has_constructor'                => true,
+            'has_named_argument_constructor' => false,
+            'targets_literal'                => 'ANNOTATION_PROPERTY',
+            'targets'                        => Target::TARGET_PROPERTY,
+            'default_property'               => 'value',
+            'properties'                     => ['value' => 'value'],
+            'attribute_types'                => [
+                'value' => [
+                    'type'      => 'array',
+                    'required'  => true,
+                ],
+                'literal' => [
+                    'type'      => 'array',
+                    'required'  => false,
+                ],
+            ],
+        ],
+        Annotation\NamedArgumentConstructor::class => [
+            'is_annotation'                  => true,
+            'has_constructor'                => false,
+            'has_named_argument_constructor' => false,
+            'targets_literal'                => 'ANNOTATION_CLASS',
+            'targets'                        => Target::TARGET_CLASS,
+            'default_property'               => null,
+            'properties'                     => [],
+            'attribute_types'                => [],
+        ],
+    ];
+
+    /**
+     * Hash-map for handle types declaration.
+     *
+     * @var array<string, string>
+     */
+    private static $typeMap = [
+        'float'     => 'double',
+        'bool'      => 'boolean',
+        // allow uppercase Boolean in honor of George Boole
+        'Boolean'   => 'boolean',
+        'int'       => 'integer',
+    ];
+
+    /**
+     * Constructs a new DocParser.
+     */
+    public function __construct()
+    {
+        $this->lexer = new DocLexer();
+    }
+
+    /**
+     * Sets the annotation names that are ignored during the parsing process.
+     *
+     * The names are supposed to be the raw names as used in the class, not the
+     * fully qualified class names.
+     *
+     * @param bool[] $names indexed by annotation name
+     *
+     * @return void
+     */
+    public function setIgnoredAnnotationNames(array $names)
+    {
+        $this->ignoredAnnotationNames = $names;
+    }
+
+    /**
+     * Sets the annotation namespaces that are ignored during the parsing process.
+     *
+     * @param bool[] $ignoredAnnotationNamespaces indexed by annotation namespace name
+     *
+     * @return void
+     */
+    public function setIgnoredAnnotationNamespaces($ignoredAnnotationNamespaces)
+    {
+        $this->ignoredAnnotationNamespaces = $ignoredAnnotationNamespaces;
+    }
+
+    /**
+     * Sets ignore on not-imported annotations.
+     *
+     * @param bool $bool
+     *
+     * @return void
+     */
+    public function setIgnoreNotImportedAnnotations($bool)
+    {
+        $this->ignoreNotImportedAnnotations = (bool) $bool;
+    }
+
+    /**
+     * Sets the default namespaces.
+     *
+     * @param string $namespace
+     *
+     * @return void
+     *
+     * @throws RuntimeException
+     */
+    public function addNamespace($namespace)
+    {
+        if ($this->imports) {
+            throw new RuntimeException('You must either use addNamespace(), or setImports(), but not both.');
+        }
+
+        $this->namespaces[] = $namespace;
+    }
+
+    /**
+     * Sets the imports.
+     *
+     * @param array<string, class-string> $imports
+     *
+     * @return void
+     *
+     * @throws RuntimeException
+     */
+    public function setImports(array $imports)
+    {
+        if ($this->namespaces) {
+            throw new RuntimeException('You must either use addNamespace(), or setImports(), but not both.');
+        }
+
+        $this->imports = $imports;
+    }
+
+    /**
+     * Sets current target context as bitmask.
+     *
+     * @param int $target
+     *
+     * @return void
+     */
+    public function setTarget($target)
+    {
+        $this->target = $target;
+    }
+
+    /**
+     * Parses the given docblock string for annotations.
+     *
+     * @param string $input   The docblock string to parse.
+     * @param string $context The parsing context.
+     *
+     * @throws AnnotationException
+     * @throws ReflectionException
+     *
+     * @phpstan-return list<object> Array of annotations. If no annotations are found, an empty array is returned.
+     */
+    public function parse($input, $context = '')
+    {
+        $pos = $this->findInitialTokenPosition($input);
+        if ($pos === null) {
+            return [];
+        }
+
+        $this->context = $context;
+
+        $this->lexer->setInput(trim(substr($input, $pos), '* /'));
+        $this->lexer->moveNext();
+
+        return $this->Annotations();
+    }
+
+    /**
+     * Finds the first valid annotation
+     *
+     * @param string $input The docblock string to parse
+     */
+    private function findInitialTokenPosition($input): ?int
+    {
+        $pos = 0;
+
+        // search for first valid annotation
+        while (($pos = strpos($input, '@', $pos)) !== false) {
+            $preceding = substr($input, $pos - 1, 1);
+
+            // if the @ is preceded by a space, a tab or * it is valid
+            if ($pos === 0 || $preceding === ' ' || $preceding === '*' || $preceding === "\t") {
+                return $pos;
+            }
+
+            $pos++;
+        }
+
+        return null;
+    }
+
+    /**
+     * Attempts to match the given token with the current lookahead token.
+     * If they match, updates the lookahead token; otherwise raises a syntax error.
+     *
+     * @param int $token Type of token.
+     *
+     * @return bool True if tokens match; false otherwise.
+     *
+     * @throws AnnotationException
+     */
+    private function match(int $token): bool
+    {
+        if (! $this->lexer->isNextToken($token)) {
+            throw $this->syntaxError($this->lexer->getLiteral($token));
+        }
+
+        return $this->lexer->moveNext();
+    }
+
+    /**
+     * Attempts to match the current lookahead token with any of the given tokens.
+     *
+     * If any of them matches, this method updates the lookahead token; otherwise
+     * a syntax error is raised.
+     *
+     * @throws AnnotationException
+     *
+     * @phpstan-param list<mixed[]> $tokens
+     */
+    private function matchAny(array $tokens): bool
+    {
+        if (! $this->lexer->isNextTokenAny($tokens)) {
+            throw $this->syntaxError(implode(' or ', array_map([$this->lexer, 'getLiteral'], $tokens)));
+        }
+
+        return $this->lexer->moveNext();
+    }
+
+    /**
+     * Generates a new syntax error.
+     *
+     * @param string       $expected Expected string.
+     * @param mixed[]|null $token    Optional token.
+     */
+    private function syntaxError(string $expected, ?array $token = null): AnnotationException
+    {
+        if ($token === null) {
+            $token = $this->lexer->lookahead;
+        }
+
+        $message  = sprintf('Expected %s, got ', $expected);
+        $message .= $this->lexer->lookahead === null
+            ? 'end of string'
+            : sprintf("'%s' at position %s", $token['value'], $token['position']);
+
+        if (strlen($this->context)) {
+            $message .= ' in ' . $this->context;
+        }
+
+        $message .= '.';
+
+        return AnnotationException::syntaxError($message);
+    }
+
+    /**
+     * Attempts to check if a class exists or not. This never goes through the PHP autoloading mechanism
+     * but uses the {@link AnnotationRegistry} to load classes.
+     *
+     * @param class-string $fqcn
+     */
+    private function classExists(string $fqcn): bool
+    {
+        if (isset($this->classExists[$fqcn])) {
+            return $this->classExists[$fqcn];
+        }
+
+        // first check if the class already exists, maybe loaded through another AnnotationReader
+        if (class_exists($fqcn, false)) {
+            return $this->classExists[$fqcn] = true;
+        }
+
+        // final check, does this class exist?
+        return $this->classExists[$fqcn] = AnnotationRegistry::loadAnnotationClass($fqcn);
+    }
+
+    /**
+     * Collects parsing metadata for a given annotation class
+     *
+     * @param class-string $name The annotation name
+     *
+     * @throws AnnotationException
+     * @throws ReflectionException
+     */
+    private function collectAnnotationMetadata(string $name): void
+    {
+        if (self::$metadataParser === null) {
+            self::$metadataParser = new self();
+
+            self::$metadataParser->setIgnoreNotImportedAnnotations(true);
+            self::$metadataParser->setIgnoredAnnotationNames($this->ignoredAnnotationNames);
+            self::$metadataParser->setImports([
+                'enum'                     => Enum::class,
+                'target'                   => Target::class,
+                'attribute'                => Attribute::class,
+                'attributes'               => Attributes::class,
+                'namedargumentconstructor' => NamedArgumentConstructor::class,
+            ]);
+
+            // Make sure that annotations from metadata are loaded
+            class_exists(Enum::class);
+            class_exists(Target::class);
+            class_exists(Attribute::class);
+            class_exists(Attributes::class);
+            class_exists(NamedArgumentConstructor::class);
+        }
+
+        $class      = new ReflectionClass($name);
+        $docComment = $class->getDocComment();
+
+        // Sets default values for annotation metadata
+        $constructor = $class->getConstructor();
+        $metadata    = [
+            'default_property' => null,
+            'has_constructor'  => $constructor !== null && $constructor->getNumberOfParameters() > 0,
+            'constructor_args' => [],
+            'properties'       => [],
+            'property_types'   => [],
+            'attribute_types'  => [],
+            'targets_literal'  => null,
+            'targets'          => Target::TARGET_ALL,
+            'is_annotation'    => strpos($docComment, '@Annotation') !== false,
+        ];
+
+        $metadata['has_named_argument_constructor'] = $metadata['has_constructor']
+            && $class->implementsInterface(NamedArgumentConstructorAnnotation::class);
+
+        // verify that the class is really meant to be an annotation
+        if ($metadata['is_annotation']) {
+            self::$metadataParser->setTarget(Target::TARGET_CLASS);
+
+            foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) {
+                if ($annotation instanceof Target) {
+                    $metadata['targets']         = $annotation->targets;
+                    $metadata['targets_literal'] = $annotation->literal;
+
+                    continue;
+                }
+
+                if ($annotation instanceof NamedArgumentConstructor) {
+                    $metadata['has_named_argument_constructor'] = $metadata['has_constructor'];
+                    if ($metadata['has_named_argument_constructor']) {
+                        // choose the first argument as the default property
+                        $metadata['default_property'] = $constructor->getParameters()[0]->getName();
+                    }
+                }
+
+                if (! ($annotation instanceof Attributes)) {
+                    continue;
+                }
+
+                foreach ($annotation->value as $attribute) {
+                    $this->collectAttributeTypeMetadata($metadata, $attribute);
+                }
+            }
+
+            // if not has a constructor will inject values into public properties
+            if ($metadata['has_constructor'] === false) {
+                // collect all public properties
+                foreach ($class->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
+                    $metadata['properties'][$property->name] = $property->name;
+
+                    $propertyComment = $property->getDocComment();
+                    if ($propertyComment === false) {
+                        continue;
+                    }
+
+                    $attribute = new Attribute();
+
+                    $attribute->required = (strpos($propertyComment, '@Required') !== false);
+                    $attribute->name     = $property->name;
+                    $attribute->type     = (strpos($propertyComment, '@var') !== false &&
+                        preg_match('/@var\s+([^\s]+)/', $propertyComment, $matches))
+                        ? $matches[1]
+                        : 'mixed';
+
+                    $this->collectAttributeTypeMetadata($metadata, $attribute);
+
+                    // checks if the property has @Enum
+                    if (strpos($propertyComment, '@Enum') === false) {
+                        continue;
+                    }
+
+                    $context = 'property ' . $class->name . '::$' . $property->name;
+
+                    self::$metadataParser->setTarget(Target::TARGET_PROPERTY);
+
+                    foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) {
+                        if (! $annotation instanceof Enum) {
+                            continue;
+                        }
+
+                        $metadata['enum'][$property->name]['value']   = $annotation->value;
+                        $metadata['enum'][$property->name]['literal'] = (! empty($annotation->literal))
+                            ? $annotation->literal
+                            : $annotation->value;
+                    }
+                }
+
+                // choose the first property as default property
+                $metadata['default_property'] = reset($metadata['properties']);
+            } elseif ($metadata['has_named_argument_constructor']) {
+                foreach ($constructor->getParameters() as $parameter) {
+                    $metadata['constructor_args'][$parameter->getName()] = [
+                        'position' => $parameter->getPosition(),
+                        'default' => $parameter->isOptional() ? $parameter->getDefaultValue() : null,
+                    ];
+                }
+            }
+        }
+
+        self::$annotationMetadata[$name] = $metadata;
+    }
+
+    /**
+     * Collects parsing metadata for a given attribute.
+     *
+     * @param mixed[] $metadata
+     */
+    private function collectAttributeTypeMetadata(array &$metadata, Attribute $attribute): void
+    {
+        // handle internal type declaration
+        $type = self::$typeMap[$attribute->type] ?? $attribute->type;
+
+        // handle the case if the property type is mixed
+        if ($type === 'mixed') {
+            return;
+        }
+
+        // Evaluate type
+        $pos = strpos($type, '<');
+        if ($pos !== false) {
+            // Checks if the property has array<type>
+            $arrayType = substr($type, $pos + 1, -1);
+            $type      = 'array';
+
+            if (isset(self::$typeMap[$arrayType])) {
+                $arrayType = self::$typeMap[$arrayType];
+            }
+
+            $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType;
+        } else {
+            // Checks if the property has type[]
+            $pos = strrpos($type, '[');
+            if ($pos !== false) {
+                $arrayType = substr($type, 0, $pos);
+                $type      = 'array';
+
+                if (isset(self::$typeMap[$arrayType])) {
+                    $arrayType = self::$typeMap[$arrayType];
+                }
+
+                $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType;
+            }
+        }
+
+        $metadata['attribute_types'][$attribute->name]['type']     = $type;
+        $metadata['attribute_types'][$attribute->name]['value']    = $attribute->type;
+        $metadata['attribute_types'][$attribute->name]['required'] = $attribute->required;
+    }
+
+    /**
+     * Annotations ::= Annotation {[ "*" ]* [Annotation]}*
+     *
+     * @throws AnnotationException
+     * @throws ReflectionException
+     *
+     * @phpstan-return list<object>
+     */
+    private function Annotations(): array
+    {
+        $annotations = [];
+
+        while ($this->lexer->lookahead !== null) {
+            if ($this->lexer->lookahead['type'] !== DocLexer::T_AT) {
+                $this->lexer->moveNext();
+                continue;
+            }
+
+            // make sure the @ is preceded by non-catchable pattern
+            if (
+                $this->lexer->token !== null &&
+                $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen(
+                    $this->lexer->token['value']
+                )
+            ) {
+                $this->lexer->moveNext();
+                continue;
+            }
+
+            // make sure the @ is followed by either a namespace separator, or
+            // an identifier token
+            $peek = $this->lexer->glimpse();
+            if (
+                ($peek === null)
+                || ($peek['type'] !== DocLexer::T_NAMESPACE_SEPARATOR && ! in_array(
+                    $peek['type'],
+                    self::$classIdentifiers,
+                    true
+                ))
+                || $peek['position'] !== $this->lexer->lookahead['position'] + 1
+            ) {
+                $this->lexer->moveNext();
+                continue;
+            }
+
+            $this->isNestedAnnotation = false;
+            $annot                    = $this->Annotation();
+            if ($annot === false) {
+                continue;
+            }
+
+            $annotations[] = $annot;
+        }
+
+        return $annotations;
+    }
+
+    /**
+     * Annotation     ::= "@" AnnotationName MethodCall
+     * AnnotationName ::= QualifiedName | SimpleName
+     * QualifiedName  ::= NameSpacePart "\" {NameSpacePart "\"}* SimpleName
+     * NameSpacePart  ::= identifier | null | false | true
+     * SimpleName     ::= identifier | null | false | true
+     *
+     * @return object|false False if it is not a valid annotation.
+     *
+     * @throws AnnotationException
+     * @throws ReflectionException
+     */
+    private function Annotation()
+    {
+        $this->match(DocLexer::T_AT);
+
+        // check if we have an annotation
+        $name = $this->Identifier();
+
+        if (
+            $this->lexer->isNextToken(DocLexer::T_MINUS)
+            && $this->lexer->nextTokenIsAdjacent()
+        ) {
+            // Annotations with dashes, such as "@foo-" or "@foo-bar", are to be discarded
+            return false;
+        }
+
+        // only process names which are not fully qualified, yet
+        // fully qualified names must start with a \
+        $originalName = $name;
+
+        if ($name[0] !== '\\') {
+            $pos          = strpos($name, '\\');
+            $alias        = ($pos === false) ? $name : substr($name, 0, $pos);
+            $found        = false;
+            $loweredAlias = strtolower($alias);
+
+            if ($this->namespaces) {
+                foreach ($this->namespaces as $namespace) {
+                    if ($this->classExists($namespace . '\\' . $name)) {
+                        $name  = $namespace . '\\' . $name;
+                        $found = true;
+                        break;
+                    }
+                }
+            } elseif (isset($this->imports[$loweredAlias])) {
+                $namespace = ltrim($this->imports[$loweredAlias], '\\');
+                $name      = ($pos !== false)
+                    ? $namespace . substr($name, $pos)
+                    : $namespace;
+                $found     = $this->classExists($name);
+            } elseif (
+                ! isset($this->ignoredAnnotationNames[$name])
+                && isset($this->imports['__NAMESPACE__'])
+                && $this->classExists($this->imports['__NAMESPACE__'] . '\\' . $name)
+            ) {
+                $name  = $this->imports['__NAMESPACE__'] . '\\' . $name;
+                $found = true;
+            } elseif (! isset($this->ignoredAnnotationNames[$name]) && $this->classExists($name)) {
+                $found = true;
+            }
+
+            if (! $found) {
+                if ($this->isIgnoredAnnotation($name)) {
+                    return false;
+                }
+
+                throw AnnotationException::semanticalError(sprintf(
+                    <<<'EXCEPTION'
+The annotation "@%s" in %s was never imported. Did you maybe forget to add a "use" statement for this annotation?
+EXCEPTION
+                    ,
+                    $name,
+                    $this->context
+                ));
+            }
+        }
+
+        $name = ltrim($name, '\\');
+
+        if (! $this->classExists($name)) {
+            throw AnnotationException::semanticalError(sprintf(
+                'The annotation "@%s" in %s does not exist, or could not be auto-loaded.',
+                $name,
+                $this->context
+            ));
+        }
+
+        // at this point, $name contains the fully qualified class name of the
+        // annotation, and it is also guaranteed that this class exists, and
+        // that it is loaded
+
+        // collects the metadata annotation only if there is not yet
+        if (! isset(self::$annotationMetadata[$name])) {
+            $this->collectAnnotationMetadata($name);
+        }
+
+        // verify that the class is really meant to be an annotation and not just any ordinary class
+        if (self::$annotationMetadata[$name]['is_annotation'] === false) {
+            if ($this->isIgnoredAnnotation($originalName) || $this->isIgnoredAnnotation($name)) {
+                return false;
+            }
+
+            throw AnnotationException::semanticalError(sprintf(
+                <<<'EXCEPTION'
+The class "%s" is not annotated with @Annotation.
+Are you sure this class can be used as annotation?
+If so, then you need to add @Annotation to the _class_ doc comment of "%s".
+If it is indeed no annotation, then you need to add @IgnoreAnnotation("%s") to the _class_ doc comment of %s.
+EXCEPTION
+                ,
+                $name,
+                $name,
+                $originalName,
+                $this->context
+            ));
+        }
+
+        //if target is nested annotation
+        $target = $this->isNestedAnnotation ? Target::TARGET_ANNOTATION : $this->target;
+
+        // Next will be nested
+        $this->isNestedAnnotation = true;
+
+        //if annotation does not support current target
+        if ((self::$annotationMetadata[$name]['targets'] & $target) === 0 && $target) {
+            throw AnnotationException::semanticalError(
+                sprintf(
+                    <<<'EXCEPTION'
+Annotation @%s is not allowed to be declared on %s. You may only use this annotation on these code elements: %s.
+EXCEPTION
+                    ,
+                    $originalName,
+                    $this->context,
+                    self::$annotationMetadata[$name]['targets_literal']
+                )
+            );
+        }
+
+        $arguments = $this->MethodCall();
+        $values    = $this->resolvePositionalValues($arguments, $name);
+
+        if (isset(self::$annotationMetadata[$name]['enum'])) {
+            // checks all declared attributes
+            foreach (self::$annotationMetadata[$name]['enum'] as $property => $enum) {
+                // checks if the attribute is a valid enumerator
+                if (isset($values[$property]) && ! in_array($values[$property], $enum['value'])) {
+                    throw AnnotationException::enumeratorError(
+                        $property,
+                        $name,
+                        $this->context,
+                        $enum['literal'],
+                        $values[$property]
+                    );
+                }
+            }
+        }
+
+        // checks all declared attributes
+        foreach (self::$annotationMetadata[$name]['attribute_types'] as $property => $type) {
+            if (
+                $property === self::$annotationMetadata[$name]['default_property']
+                && ! isset($values[$property]) && isset($values['value'])
+            ) {
+                $property = 'value';
+            }
+
+            // handle a not given attribute or null value
+            if (! isset($values[$property])) {
+                if ($type['required']) {
+                    throw AnnotationException::requiredError(
+                        $property,
+                        $originalName,
+                        $this->context,
+                        'a(n) ' . $type['value']
+                    );
+                }
+
+                continue;
+            }
+
+            if ($type['type'] === 'array') {
+                // handle the case of a single value
+                if (! is_array($values[$property])) {
+                    $values[$property] = [$values[$property]];
+                }
+
+                // checks if the attribute has array type declaration, such as "array<string>"
+                if (isset($type['array_type'])) {
+                    foreach ($values[$property] as $item) {
+                        if (gettype($item) !== $type['array_type'] && ! $item instanceof $type['array_type']) {
+                            throw AnnotationException::attributeTypeError(
+                                $property,
+                                $originalName,
+                                $this->context,
+                                'either a(n) ' . $type['array_type'] . ', or an array of ' . $type['array_type'] . 's',
+                                $item
+                            );
+                        }
+                    }
+                }
+            } elseif (gettype($values[$property]) !== $type['type'] && ! $values[$property] instanceof $type['type']) {
+                throw AnnotationException::attributeTypeError(
+                    $property,
+                    $originalName,
+                    $this->context,
+                    'a(n) ' . $type['value'],
+                    $values[$property]
+                );
+            }
+        }
+
+        if (self::$annotationMetadata[$name]['has_named_argument_constructor']) {
+            if (PHP_VERSION_ID >= 80000) {
+                return new $name(...$values);
+            }
+
+            $positionalValues = [];
+            foreach (self::$annotationMetadata[$name]['constructor_args'] as $property => $parameter) {
+                $positionalValues[$parameter['position']] = $parameter['default'];
+            }
+
+            foreach ($values as $property => $value) {
+                if (! isset(self::$annotationMetadata[$name]['constructor_args'][$property])) {
+                    throw AnnotationException::creationError(sprintf(
+                        <<<'EXCEPTION'
+The annotation @%s declared on %s does not have a property named "%s"
+that can be set through its named arguments constructor.
+Available named arguments: %s
+EXCEPTION
+                        ,
+                        $originalName,
+                        $this->context,
+                        $property,
+                        implode(', ', array_keys(self::$annotationMetadata[$name]['constructor_args']))
+                    ));
+                }
+
+                $positionalValues[self::$annotationMetadata[$name]['constructor_args'][$property]['position']] = $value;
+            }
+
+            return new $name(...$positionalValues);
+        }
+
+        // check if the annotation expects values via the constructor,
+        // or directly injected into public properties
+        if (self::$annotationMetadata[$name]['has_constructor'] === true) {
+            return new $name($values);
+        }
+
+        $instance = new $name();
+
+        foreach ($values as $property => $value) {
+            if (! isset(self::$annotationMetadata[$name]['properties'][$property])) {
+                if ($property !== 'value') {
+                    throw AnnotationException::creationError(sprintf(
+                        <<<'EXCEPTION'
+The annotation @%s declared on %s does not have a property named "%s".
+Available properties: %s
+EXCEPTION
+                        ,
+                        $originalName,
+                        $this->context,
+                        $property,
+                        implode(', ', self::$annotationMetadata[$name]['properties'])
+                    ));
+                }
+
+                // handle the case if the property has no annotations
+                $property = self::$annotationMetadata[$name]['default_property'];
+                if (! $property) {
+                    throw AnnotationException::creationError(sprintf(
+                        'The annotation @%s declared on %s does not accept any values, but got %s.',
+                        $originalName,
+                        $this->context,
+                        json_encode($values)
+                    ));
+                }
+            }
+
+            $instance->{$property} = $value;
+        }
+
+        return $instance;
+    }
+
+    /**
+     * MethodCall ::= ["(" [Values] ")"]
+     *
+     * @return mixed[]
+     *
+     * @throws AnnotationException
+     * @throws ReflectionException
+     */
+    private function MethodCall(): array
+    {
+        $values = [];
+
+        if (! $this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) {
+            return $values;
+        }
+
+        $this->match(DocLexer::T_OPEN_PARENTHESIS);
+
+        if (! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) {
+            $values = $this->Values();
+        }
+
+        $this->match(DocLexer::T_CLOSE_PARENTHESIS);
+
+        return $values;
+    }
+
+    /**
+     * Values ::= Array | Value {"," Value}* [","]
+     *
+     * @return mixed[]
+     *
+     * @throws AnnotationException
+     * @throws ReflectionException
+     */
+    private function Values(): array
+    {
+        $values = [$this->Value()];
+
+        while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
+            $this->match(DocLexer::T_COMMA);
+
+            if ($this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) {
+                break;
+            }
+
+            $token = $this->lexer->lookahead;
+            $value = $this->Value();
+
+            $values[] = $value;
+        }
+
+        $namedArguments      = [];
+        $positionalArguments = [];
+        foreach ($values as $k => $value) {
+            if (is_object($value) && $value instanceof stdClass) {
+                $namedArguments[$value->name] = $value->value;
+            } else {
+                $positionalArguments[$k] = $value;
+            }
+        }
+
+        return ['named_arguments' => $namedArguments, 'positional_arguments' => $positionalArguments];
+    }
+
+    /**
+     * Constant ::= integer | string | float | boolean
+     *
+     * @return mixed
+     *
+     * @throws AnnotationException
+     */
+    private function Constant()
+    {
+        $identifier = $this->Identifier();
+
+        if (! defined($identifier) && strpos($identifier, '::') !== false && $identifier[0] !== '\\') {
+            [$className, $const] = explode('::', $identifier);
+
+            $pos          = strpos($className, '\\');
+            $alias        = ($pos === false) ? $className : substr($className, 0, $pos);
+            $found        = false;
+            $loweredAlias = strtolower($alias);
+
+            switch (true) {
+                case ! empty($this->namespaces):
+                    foreach ($this->namespaces as $ns) {
+                        if (class_exists($ns . '\\' . $className) || interface_exists($ns . '\\' . $className)) {
+                            $className = $ns . '\\' . $className;
+                            $found     = true;
+                            break;
+                        }
+                    }
+
+                    break;
+
+                case isset($this->imports[$loweredAlias]):
+                    $found     = true;
+                    $className = ($pos !== false)
+                        ? $this->imports[$loweredAlias] . substr($className, $pos)
+                        : $this->imports[$loweredAlias];
+                    break;
+
+                default:
+                    if (isset($this->imports['__NAMESPACE__'])) {
+                        $ns = $this->imports['__NAMESPACE__'];
+
+                        if (class_exists($ns . '\\' . $className) || interface_exists($ns . '\\' . $className)) {
+                            $className = $ns . '\\' . $className;
+                            $found     = true;
+                        }
+                    }
+
+                    break;
+            }
+
+            if ($found) {
+                $identifier = $className . '::' . $const;
+            }
+        }
+
+        /**
+         * Checks if identifier ends with ::class and remove the leading backslash if it exists.
+         */
+        if (
+            $this->identifierEndsWithClassConstant($identifier) &&
+            ! $this->identifierStartsWithBackslash($identifier)
+        ) {
+            return substr($identifier, 0, $this->getClassConstantPositionInIdentifier($identifier));
+        }
+
+        if ($this->identifierEndsWithClassConstant($identifier) && $this->identifierStartsWithBackslash($identifier)) {
+            return substr($identifier, 1, $this->getClassConstantPositionInIdentifier($identifier) - 1);
+        }
+
+        if (! defined($identifier)) {
+            throw AnnotationException::semanticalErrorConstants($identifier, $this->context);
+        }
+
+        return constant($identifier);
+    }
+
+    private function identifierStartsWithBackslash(string $identifier): bool
+    {
+        return $identifier[0] === '\\';
+    }
+
+    private function identifierEndsWithClassConstant(string $identifier): bool
+    {
+        return $this->getClassConstantPositionInIdentifier($identifier) === strlen($identifier) - strlen('::class');
+    }
+
+    /**
+     * @return int|false
+     */
+    private function getClassConstantPositionInIdentifier(string $identifier)
+    {
+        return stripos($identifier, '::class');
+    }
+
+    /**
+     * Identifier ::= string
+     *
+     * @throws AnnotationException
+     */
+    private function Identifier(): string
+    {
+        // check if we have an annotation
+        if (! $this->lexer->isNextTokenAny(self::$classIdentifiers)) {
+            throw $this->syntaxError('namespace separator or identifier');
+        }
+
+        $this->lexer->moveNext();
+
+        $className = $this->lexer->token['value'];
+
+        while (
+            $this->lexer->lookahead !== null &&
+            $this->lexer->lookahead['position'] === ($this->lexer->token['position'] +
+            strlen($this->lexer->token['value'])) &&
+            $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)
+        ) {
+            $this->match(DocLexer::T_NAMESPACE_SEPARATOR);
+            $this->matchAny(self::$classIdentifiers);
+
+            $className .= '\\' . $this->lexer->token['value'];
+        }
+
+        return $className;
+    }
+
+    /**
+     * Value ::= PlainValue | FieldAssignment
+     *
+     * @return mixed
+     *
+     * @throws AnnotationException
+     * @throws ReflectionException
+     */
+    private function Value()
+    {
+        $peek = $this->lexer->glimpse();
+
+        if ($peek['type'] === DocLexer::T_EQUALS) {
+            return $this->FieldAssignment();
+        }
+
+        return $this->PlainValue();
+    }
+
+    /**
+     * PlainValue ::= integer | string | float | boolean | Array | Annotation
+     *
+     * @return mixed
+     *
+     * @throws AnnotationException
+     * @throws ReflectionException
+     */
+    private function PlainValue()
+    {
+        if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) {
+            return $this->Arrayx();
+        }
+
+        if ($this->lexer->isNextToken(DocLexer::T_AT)) {
+            return $this->Annotation();
+        }
+
+        if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
+            return $this->Constant();
+        }
+
+        switch ($this->lexer->lookahead['type']) {
+            case DocLexer::T_STRING:
+                $this->match(DocLexer::T_STRING);
+
+                return $this->lexer->token['value'];
+
+            case DocLexer::T_INTEGER:
+                $this->match(DocLexer::T_INTEGER);
+
+                return (int) $this->lexer->token['value'];
+
+            case DocLexer::T_FLOAT:
+                $this->match(DocLexer::T_FLOAT);
+
+                return (float) $this->lexer->token['value'];
+
+            case DocLexer::T_TRUE:
+                $this->match(DocLexer::T_TRUE);
+
+                return true;
+
+            case DocLexer::T_FALSE:
+                $this->match(DocLexer::T_FALSE);
+
+                return false;
+
+            case DocLexer::T_NULL:
+                $this->match(DocLexer::T_NULL);
+
+                return null;
+
+            default:
+                throw $this->syntaxError('PlainValue');
+        }
+    }
+
+    /**
+     * FieldAssignment ::= FieldName "=" PlainValue
+     * FieldName ::= identifier
+     *
+     * @throws AnnotationException
+     * @throws ReflectionException
+     */
+    private function FieldAssignment(): stdClass
+    {
+        $this->match(DocLexer::T_IDENTIFIER);
+        $fieldName = $this->lexer->token['value'];
+
+        $this->match(DocLexer::T_EQUALS);
+
+        $item        = new stdClass();
+        $item->name  = $fieldName;
+        $item->value = $this->PlainValue();
+
+        return $item;
+    }
+
+    /**
+     * Array ::= "{" ArrayEntry {"," ArrayEntry}* [","] "}"
+     *
+     * @return mixed[]
+     *
+     * @throws AnnotationException
+     * @throws ReflectionException
+     */
+    private function Arrayx(): array
+    {
+        $array = $values = [];
+
+        $this->match(DocLexer::T_OPEN_CURLY_BRACES);
+
+        // If the array is empty, stop parsing and return.
+        if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) {
+            $this->match(DocLexer::T_CLOSE_CURLY_BRACES);
+
+            return $array;
+        }
+
+        $values[] = $this->ArrayEntry();
+
+        while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
+            $this->match(DocLexer::T_COMMA);
+
+            // optional trailing comma
+            if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) {
+                break;
+            }
+
+            $values[] = $this->ArrayEntry();
+        }
+
+        $this->match(DocLexer::T_CLOSE_CURLY_BRACES);
+
+        foreach ($values as $value) {
+            [$key, $val] = $value;
+
+            if ($key !== null) {
+                $array[$key] = $val;
+            } else {
+                $array[] = $val;
+            }
+        }
+
+        return $array;
+    }
+
+    /**
+     * ArrayEntry ::= Value | KeyValuePair
+     * KeyValuePair ::= Key ("=" | ":") PlainValue | Constant
+     * Key ::= string | integer | Constant
+     *
+     * @throws AnnotationException
+     * @throws ReflectionException
+     *
+     * @phpstan-return array{mixed, mixed}
+     */
+    private function ArrayEntry(): array
+    {
+        $peek = $this->lexer->glimpse();
+
+        if (
+            $peek['type'] === DocLexer::T_EQUALS
+                || $peek['type'] === DocLexer::T_COLON
+        ) {
+            if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
+                $key = $this->Constant();
+            } else {
+                $this->matchAny([DocLexer::T_INTEGER, DocLexer::T_STRING]);
+                $key = $this->lexer->token['value'];
+            }
+
+            $this->matchAny([DocLexer::T_EQUALS, DocLexer::T_COLON]);
+
+            return [$key, $this->PlainValue()];
+        }
+
+        return [null, $this->Value()];
+    }
+
+    /**
+     * Checks whether the given $name matches any ignored annotation name or namespace
+     */
+    private function isIgnoredAnnotation(string $name): bool
+    {
+        if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$name])) {
+            return true;
+        }
+
+        foreach (array_keys($this->ignoredAnnotationNamespaces) as $ignoredAnnotationNamespace) {
+            $ignoredAnnotationNamespace = rtrim($ignoredAnnotationNamespace, '\\') . '\\';
+
+            if (stripos(rtrim($name, '\\') . '\\', $ignoredAnnotationNamespace) === 0) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Resolve positional arguments (without name) to named ones
+     *
+     * @param array<string,mixed> $arguments
+     *
+     * @return array<string,mixed>
+     */
+    private function resolvePositionalValues(array $arguments, string $name): array
+    {
+        $positionalArguments = $arguments['positional_arguments'] ?? [];
+        $values              = $arguments['named_arguments'] ?? [];
+
+        if (
+            self::$annotationMetadata[$name]['has_named_argument_constructor']
+            && self::$annotationMetadata[$name]['default_property'] !== null
+        ) {
+            // We must ensure that we don't have positional arguments after named ones
+            $positions    = array_keys($positionalArguments);
+            $lastPosition = null;
+            foreach ($positions as $position) {
+                if (
+                    ($lastPosition === null && $position !== 0) ||
+                    ($lastPosition !== null && $position !== $lastPosition + 1)
+                ) {
+                    throw $this->syntaxError('Positional arguments after named arguments is not allowed');
+                }
+
+                $lastPosition = $position;
+            }
+
+            foreach (self::$annotationMetadata[$name]['constructor_args'] as $property => $parameter) {
+                $position = $parameter['position'];
+                if (isset($values[$property]) || ! isset($positionalArguments[$position])) {
+                    continue;
+                }
+
+                $values[$property] = $positionalArguments[$position];
+            }
+        } else {
+            if (count($positionalArguments) > 0 && ! isset($values['value'])) {
+                if (count($positionalArguments) === 1) {
+                    $value = array_pop($positionalArguments);
+                } else {
+                    $value = array_values($positionalArguments);
+                }
+
+                $values['value'] = $value;
+            }
+        }
+
+        return $values;
+    }
+}

+ 315 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php

@@ -0,0 +1,315 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use InvalidArgumentException;
+use ReflectionClass;
+use ReflectionMethod;
+use ReflectionProperty;
+use RuntimeException;
+
+use function chmod;
+use function file_put_contents;
+use function filemtime;
+use function gettype;
+use function is_dir;
+use function is_file;
+use function is_int;
+use function is_writable;
+use function mkdir;
+use function rename;
+use function rtrim;
+use function serialize;
+use function sha1;
+use function sprintf;
+use function strtr;
+use function tempnam;
+use function uniqid;
+use function unlink;
+use function var_export;
+
+/**
+ * File cache reader for annotations.
+ *
+ * @deprecated the FileCacheReader is deprecated and will be removed
+ *             in version 2.0.0 of doctrine/annotations. Please use the
+ *             {@see \Doctrine\Common\Annotations\CachedReader} instead.
+ */
+class FileCacheReader implements Reader
+{
+    /** @var Reader */
+    private $reader;
+
+    /** @var string */
+    private $dir;
+
+    /** @var bool */
+    private $debug;
+
+    /** @phpstan-var array<string, list<object>> */
+    private $loadedAnnotations = [];
+
+    /** @var array<string, string> */
+    private $classNameHashes = [];
+
+    /** @var int */
+    private $umask;
+
+    /**
+     * @param string $cacheDir
+     * @param bool   $debug
+     * @param int    $umask
+     *
+     * @throws InvalidArgumentException
+     */
+    public function __construct(Reader $reader, $cacheDir, $debug = false, $umask = 0002)
+    {
+        if (! is_int($umask)) {
+            throw new InvalidArgumentException(sprintf(
+                'The parameter umask must be an integer, was: %s',
+                gettype($umask)
+            ));
+        }
+
+        $this->reader = $reader;
+        $this->umask  = $umask;
+
+        if (! is_dir($cacheDir) && ! @mkdir($cacheDir, 0777 & (~$this->umask), true)) {
+            throw new InvalidArgumentException(sprintf(
+                'The directory "%s" does not exist and could not be created.',
+                $cacheDir
+            ));
+        }
+
+        $this->dir   = rtrim($cacheDir, '\\/');
+        $this->debug = $debug;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getClassAnnotations(ReflectionClass $class)
+    {
+        if (! isset($this->classNameHashes[$class->name])) {
+            $this->classNameHashes[$class->name] = sha1($class->name);
+        }
+
+        $key = $this->classNameHashes[$class->name];
+
+        if (isset($this->loadedAnnotations[$key])) {
+            return $this->loadedAnnotations[$key];
+        }
+
+        $path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
+        if (! is_file($path)) {
+            $annot = $this->reader->getClassAnnotations($class);
+            $this->saveCacheFile($path, $annot);
+
+            return $this->loadedAnnotations[$key] = $annot;
+        }
+
+        $filename = $class->getFilename();
+        if (
+            $this->debug
+            && $filename !== false
+            && filemtime($path) < filemtime($filename)
+        ) {
+            @unlink($path);
+
+            $annot = $this->reader->getClassAnnotations($class);
+            $this->saveCacheFile($path, $annot);
+
+            return $this->loadedAnnotations[$key] = $annot;
+        }
+
+        return $this->loadedAnnotations[$key] = include $path;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPropertyAnnotations(ReflectionProperty $property)
+    {
+        $class = $property->getDeclaringClass();
+        if (! isset($this->classNameHashes[$class->name])) {
+            $this->classNameHashes[$class->name] = sha1($class->name);
+        }
+
+        $key = $this->classNameHashes[$class->name] . '$' . $property->getName();
+
+        if (isset($this->loadedAnnotations[$key])) {
+            return $this->loadedAnnotations[$key];
+        }
+
+        $path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
+        if (! is_file($path)) {
+            $annot = $this->reader->getPropertyAnnotations($property);
+            $this->saveCacheFile($path, $annot);
+
+            return $this->loadedAnnotations[$key] = $annot;
+        }
+
+        $filename = $class->getFilename();
+        if (
+            $this->debug
+            && $filename !== false
+            && filemtime($path) < filemtime($filename)
+        ) {
+            @unlink($path);
+
+            $annot = $this->reader->getPropertyAnnotations($property);
+            $this->saveCacheFile($path, $annot);
+
+            return $this->loadedAnnotations[$key] = $annot;
+        }
+
+        return $this->loadedAnnotations[$key] = include $path;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethodAnnotations(ReflectionMethod $method)
+    {
+        $class = $method->getDeclaringClass();
+        if (! isset($this->classNameHashes[$class->name])) {
+            $this->classNameHashes[$class->name] = sha1($class->name);
+        }
+
+        $key = $this->classNameHashes[$class->name] . '#' . $method->getName();
+
+        if (isset($this->loadedAnnotations[$key])) {
+            return $this->loadedAnnotations[$key];
+        }
+
+        $path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
+        if (! is_file($path)) {
+            $annot = $this->reader->getMethodAnnotations($method);
+            $this->saveCacheFile($path, $annot);
+
+            return $this->loadedAnnotations[$key] = $annot;
+        }
+
+        $filename = $class->getFilename();
+        if (
+            $this->debug
+            && $filename !== false
+            && filemtime($path) < filemtime($filename)
+        ) {
+            @unlink($path);
+
+            $annot = $this->reader->getMethodAnnotations($method);
+            $this->saveCacheFile($path, $annot);
+
+            return $this->loadedAnnotations[$key] = $annot;
+        }
+
+        return $this->loadedAnnotations[$key] = include $path;
+    }
+
+    /**
+     * Saves the cache file.
+     *
+     * @param string $path
+     * @param mixed  $data
+     *
+     * @return void
+     */
+    private function saveCacheFile($path, $data)
+    {
+        if (! is_writable($this->dir)) {
+            throw new InvalidArgumentException(sprintf(
+                <<<'EXCEPTION'
+The directory "%s" is not writable. Both the webserver and the console user need access.
+You can manage access rights for multiple users with "chmod +a".
+If your system does not support this, check out the acl package.,
+EXCEPTION
+                ,
+                $this->dir
+            ));
+        }
+
+        $tempfile = tempnam($this->dir, uniqid('', true));
+
+        if ($tempfile === false) {
+            throw new RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir));
+        }
+
+        @chmod($tempfile, 0666 & (~$this->umask));
+
+        $written = file_put_contents(
+            $tempfile,
+            '<?php return unserialize(' . var_export(serialize($data), true) . ');'
+        );
+
+        if ($written === false) {
+            throw new RuntimeException(sprintf('Unable to write cached file to: %s', $tempfile));
+        }
+
+        @chmod($tempfile, 0666 & (~$this->umask));
+
+        if (rename($tempfile, $path) === false) {
+            @unlink($tempfile);
+
+            throw new RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getClassAnnotation(ReflectionClass $class, $annotationName)
+    {
+        $annotations = $this->getClassAnnotations($class);
+
+        foreach ($annotations as $annotation) {
+            if ($annotation instanceof $annotationName) {
+                return $annotation;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
+    {
+        $annotations = $this->getMethodAnnotations($method);
+
+        foreach ($annotations as $annotation) {
+            if ($annotation instanceof $annotationName) {
+                return $annotation;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
+    {
+        $annotations = $this->getPropertyAnnotations($property);
+
+        foreach ($annotations as $annotation) {
+            if ($annotation instanceof $annotationName) {
+                return $annotation;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Clears loaded annotations.
+     *
+     * @return void
+     */
+    public function clearLoadedAnnotations()
+    {
+        $this->loadedAnnotations = [];
+    }
+}

+ 171 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.php

@@ -0,0 +1,171 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Common\Annotations;
+
+/**
+ *  A list of annotations that are implicitly ignored during the parsing process.
+ *
+ *  All names are case sensitive.
+ */
+final class ImplicitlyIgnoredAnnotationNames
+{
+    private const Reserved = [
+        'Annotation' => true,
+        'Attribute'  => true,
+        'Attributes' => true,
+        /* Can we enable this? 'Enum' => true, */
+        'Required'   => true,
+        'Target'     => true,
+    ];
+
+    private const WidelyUsedNonStandard = [
+        'fix'      => true,
+        'fixme'    => true,
+        'override' => true,
+    ];
+
+    private const PhpDocumentor1 = [
+        'abstract'   => true,
+        'access'     => true,
+        'code'       => true,
+        'deprec'     => true,
+        'endcode'    => true,
+        'exception'  => true,
+        'final'      => true,
+        'ingroup'    => true,
+        'inheritdoc' => true,
+        'inheritDoc' => true,
+        'magic'      => true,
+        'name'       => true,
+        'private'    => true,
+        'static'     => true,
+        'staticvar'  => true,
+        'staticVar'  => true,
+        'toc'        => true,
+        'tutorial'   => true,
+        'throw'      => true,
+    ];
+
+    private const PhpDocumentor2 = [
+        'api'            => true,
+        'author'         => true,
+        'category'       => true,
+        'copyright'      => true,
+        'deprecated'     => true,
+        'example'        => true,
+        'filesource'     => true,
+        'global'         => true,
+        'ignore'         => true,
+        /* Can we enable this? 'index' => true, */
+        'internal'       => true,
+        'license'        => true,
+        'link'           => true,
+        'method'         => true,
+        'package'        => true,
+        'param'          => true,
+        'property'       => true,
+        'property-read'  => true,
+        'property-write' => true,
+        'return'         => true,
+        'see'            => true,
+        'since'          => true,
+        'source'         => true,
+        'subpackage'     => true,
+        'throws'         => true,
+        'todo'           => true,
+        'TODO'           => true,
+        'usedby'         => true,
+        'uses'           => true,
+        'var'            => true,
+        'version'        => true,
+    ];
+
+    private const PHPUnit = [
+        'author'                         => true,
+        'after'                          => true,
+        'afterClass'                     => true,
+        'backupGlobals'                  => true,
+        'backupStaticAttributes'         => true,
+        'before'                         => true,
+        'beforeClass'                    => true,
+        'codeCoverageIgnore'             => true,
+        'codeCoverageIgnoreStart'        => true,
+        'codeCoverageIgnoreEnd'          => true,
+        'covers'                         => true,
+        'coversDefaultClass'             => true,
+        'coversNothing'                  => true,
+        'dataProvider'                   => true,
+        'depends'                        => true,
+        'doesNotPerformAssertions'       => true,
+        'expectedException'              => true,
+        'expectedExceptionCode'          => true,
+        'expectedExceptionMessage'       => true,
+        'expectedExceptionMessageRegExp' => true,
+        'group'                          => true,
+        'large'                          => true,
+        'medium'                         => true,
+        'preserveGlobalState'            => true,
+        'requires'                       => true,
+        'runTestsInSeparateProcesses'    => true,
+        'runInSeparateProcess'           => true,
+        'small'                          => true,
+        'test'                           => true,
+        'testdox'                        => true,
+        'testWith'                       => true,
+        'ticket'                         => true,
+        'uses'                           => true,
+    ];
+
+    private const PhpCheckStyle = ['SuppressWarnings' => true];
+
+    private const PhpStorm = ['noinspection' => true];
+
+    private const PEAR = ['package_version' => true];
+
+    private const PlainUML = [
+        'startuml' => true,
+        'enduml'   => true,
+    ];
+
+    private const Symfony = ['experimental' => true];
+
+    private const PhpCodeSniffer = [
+        'codingStandardsIgnoreStart' => true,
+        'codingStandardsIgnoreEnd'   => true,
+    ];
+
+    private const SlevomatCodingStandard = ['phpcsSuppress' => true];
+
+    private const PhpStan = [
+        'extends' => true,
+        'implements' => true,
+        'template' => true,
+        'use' => true,
+    ];
+
+    private const Phan = ['suppress' => true];
+
+    private const Rector = ['noRector' => true];
+
+    public const LIST = self::Reserved
+        + self::WidelyUsedNonStandard
+        + self::PhpDocumentor1
+        + self::PhpDocumentor2
+        + self::PHPUnit
+        + self::PhpCheckStyle
+        + self::PhpStorm
+        + self::PEAR
+        + self::PlainUML
+        + self::Symfony
+        + self::SlevomatCodingStandard
+        + self::PhpCodeSniffer
+        + self::PhpStan
+        + self::Phan
+        + self::Rector;
+
+    private function __construct()
+    {
+    }
+}

+ 100 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php

@@ -0,0 +1,100 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use ReflectionClass;
+use ReflectionMethod;
+use ReflectionProperty;
+
+use function call_user_func_array;
+use function get_class;
+
+/**
+ * Allows the reader to be used in-place of Doctrine's reader.
+ */
+class IndexedReader implements Reader
+{
+    /** @var Reader */
+    private $delegate;
+
+    public function __construct(Reader $reader)
+    {
+        $this->delegate = $reader;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getClassAnnotations(ReflectionClass $class)
+    {
+        $annotations = [];
+        foreach ($this->delegate->getClassAnnotations($class) as $annot) {
+            $annotations[get_class($annot)] = $annot;
+        }
+
+        return $annotations;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getClassAnnotation(ReflectionClass $class, $annotation)
+    {
+        return $this->delegate->getClassAnnotation($class, $annotation);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethodAnnotations(ReflectionMethod $method)
+    {
+        $annotations = [];
+        foreach ($this->delegate->getMethodAnnotations($method) as $annot) {
+            $annotations[get_class($annot)] = $annot;
+        }
+
+        return $annotations;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethodAnnotation(ReflectionMethod $method, $annotation)
+    {
+        return $this->delegate->getMethodAnnotation($method, $annotation);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPropertyAnnotations(ReflectionProperty $property)
+    {
+        $annotations = [];
+        foreach ($this->delegate->getPropertyAnnotations($property) as $annot) {
+            $annotations[get_class($annot)] = $annot;
+        }
+
+        return $annotations;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPropertyAnnotation(ReflectionProperty $property, $annotation)
+    {
+        return $this->delegate->getPropertyAnnotation($property, $annotation);
+    }
+
+    /**
+     * Proxies all methods to the delegate.
+     *
+     * @param string  $method
+     * @param mixed[] $args
+     *
+     * @return mixed
+     */
+    public function __call($method, $args)
+    {
+        return call_user_func_array([$this->delegate, $method], $args);
+    }
+}

+ 14 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/NamedArgumentConstructorAnnotation.php

@@ -0,0 +1,14 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+/**
+ * Marker interface for PHP7/PHP8 compatible support
+ * for named arguments (and constructor property promotion).
+ *
+ * @deprecated Implementing this interface is deprecated
+ *             Use the Annotation @NamedArgumentConstructor instead
+ */
+interface NamedArgumentConstructorAnnotation
+{
+}

+ 92 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php

@@ -0,0 +1,92 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use ReflectionClass;
+use ReflectionFunction;
+use SplFileObject;
+
+use function is_file;
+use function method_exists;
+use function preg_quote;
+use function preg_replace;
+
+/**
+ * Parses a file for namespaces/use/class declarations.
+ */
+final class PhpParser
+{
+    /**
+     * Parses a class.
+     *
+     * @deprecated use parseUseStatements instead
+     *
+     * @param ReflectionClass $class A <code>ReflectionClass</code> object.
+     *
+     * @return array<string, class-string> A list with use statements in the form (Alias => FQN).
+     */
+    public function parseClass(ReflectionClass $class)
+    {
+        return $this->parseUseStatements($class);
+    }
+
+    /**
+     * Parse a class or function for use statements.
+     *
+     * @param ReflectionClass|ReflectionFunction $reflection
+     *
+     * @psalm-return array<string, string> a list with use statements in the form (Alias => FQN).
+     */
+    public function parseUseStatements($reflection): array
+    {
+        if (method_exists($reflection, 'getUseStatements')) {
+            return $reflection->getUseStatements();
+        }
+
+        $filename = $reflection->getFileName();
+
+        if ($filename === false) {
+            return [];
+        }
+
+        $content = $this->getFileContent($filename, $reflection->getStartLine());
+
+        if ($content === null) {
+            return [];
+        }
+
+        $namespace = preg_quote($reflection->getNamespaceName());
+        $content   = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content);
+        $tokenizer = new TokenParser('<?php ' . $content);
+
+        return $tokenizer->parseUseStatements($reflection->getNamespaceName());
+    }
+
+    /**
+     * Gets the content of the file right up to the given line number.
+     *
+     * @param string $filename   The name of the file to load.
+     * @param int    $lineNumber The number of lines to read from file.
+     *
+     * @return string|null The content of the file or null if the file does not exist.
+     */
+    private function getFileContent($filename, $lineNumber)
+    {
+        if (! is_file($filename)) {
+            return null;
+        }
+
+        $content = '';
+        $lineCnt = 0;
+        $file    = new SplFileObject($filename);
+        while (! $file->eof()) {
+            if ($lineCnt++ === $lineNumber) {
+                break;
+            }
+
+            $content .= $file->fgets();
+        }
+
+        return $content;
+    }
+}

+ 80 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php

@@ -0,0 +1,80 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use ReflectionClass;
+use ReflectionMethod;
+use ReflectionProperty;
+
+/**
+ * Interface for annotation readers.
+ */
+interface Reader
+{
+    /**
+     * Gets the annotations applied to a class.
+     *
+     * @param ReflectionClass $class The ReflectionClass of the class from which
+     * the class annotations should be read.
+     *
+     * @return array<object> An array of Annotations.
+     */
+    public function getClassAnnotations(ReflectionClass $class);
+
+    /**
+     * Gets a class annotation.
+     *
+     * @param ReflectionClass $class          The ReflectionClass of the class from which
+     *          the class annotations should be read.
+     * @param class-string<T> $annotationName The name of the annotation.
+     *
+     * @return T|null The Annotation or NULL, if the requested annotation does not exist.
+     *
+     * @template T
+     */
+    public function getClassAnnotation(ReflectionClass $class, $annotationName);
+
+    /**
+     * Gets the annotations applied to a method.
+     *
+     * @param ReflectionMethod $method The ReflectionMethod of the method from which
+     * the annotations should be read.
+     *
+     * @return array<object> An array of Annotations.
+     */
+    public function getMethodAnnotations(ReflectionMethod $method);
+
+    /**
+     * Gets a method annotation.
+     *
+     * @param ReflectionMethod $method         The ReflectionMethod to read the annotations from.
+     * @param class-string<T>  $annotationName The name of the annotation.
+     *
+     * @return T|null The Annotation or NULL, if the requested annotation does not exist.
+     *
+     * @template T
+     */
+    public function getMethodAnnotation(ReflectionMethod $method, $annotationName);
+
+    /**
+     * Gets the annotations applied to a property.
+     *
+     * @param ReflectionProperty $property The ReflectionProperty of the property
+     * from which the annotations should be read.
+     *
+     * @return array<object> An array of Annotations.
+     */
+    public function getPropertyAnnotations(ReflectionProperty $property);
+
+    /**
+     * Gets a property annotation.
+     *
+     * @param ReflectionProperty $property       The ReflectionProperty to read the annotations from.
+     * @param class-string<T>    $annotationName The name of the annotation.
+     *
+     * @return T|null The Annotation or NULL, if the requested annotation does not exist.
+     *
+     * @template T
+     */
+    public function getPropertyAnnotation(ReflectionProperty $property, $annotationName);
+}

+ 114 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php

@@ -0,0 +1,114 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use ReflectionClass;
+use ReflectionMethod;
+use ReflectionProperty;
+
+/**
+ * Simple Annotation Reader.
+ *
+ * This annotation reader is intended to be used in projects where you have
+ * full-control over all annotations that are available.
+ *
+ * @deprecated Deprecated in favour of using AnnotationReader
+ */
+class SimpleAnnotationReader implements Reader
+{
+    /** @var DocParser */
+    private $parser;
+
+    /**
+     * Initializes a new SimpleAnnotationReader.
+     */
+    public function __construct()
+    {
+        $this->parser = new DocParser();
+        $this->parser->setIgnoreNotImportedAnnotations(true);
+    }
+
+    /**
+     * Adds a namespace in which we will look for annotations.
+     *
+     * @param string $namespace
+     *
+     * @return void
+     */
+    public function addNamespace($namespace)
+    {
+        $this->parser->addNamespace($namespace);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getClassAnnotations(ReflectionClass $class)
+    {
+        return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethodAnnotations(ReflectionMethod $method)
+    {
+        return $this->parser->parse(
+            $method->getDocComment(),
+            'method ' . $method->getDeclaringClass()->name . '::' . $method->getName() . '()'
+        );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPropertyAnnotations(ReflectionProperty $property)
+    {
+        return $this->parser->parse(
+            $property->getDocComment(),
+            'property ' . $property->getDeclaringClass()->name . '::$' . $property->getName()
+        );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getClassAnnotation(ReflectionClass $class, $annotationName)
+    {
+        foreach ($this->getClassAnnotations($class) as $annot) {
+            if ($annot instanceof $annotationName) {
+                return $annot;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
+    {
+        foreach ($this->getMethodAnnotations($method) as $annot) {
+            if ($annot instanceof $annotationName) {
+                return $annot;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
+    {
+        foreach ($this->getPropertyAnnotations($property) as $annot) {
+            if ($annot instanceof $annotationName) {
+                return $annot;
+            }
+        }
+
+        return null;
+    }
+}

+ 208 - 0
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php

@@ -0,0 +1,208 @@
+<?php
+
+namespace Doctrine\Common\Annotations;
+
+use function array_merge;
+use function count;
+use function explode;
+use function strtolower;
+use function token_get_all;
+
+use const PHP_VERSION_ID;
+use const T_AS;
+use const T_COMMENT;
+use const T_DOC_COMMENT;
+use const T_NAME_FULLY_QUALIFIED;
+use const T_NAME_QUALIFIED;
+use const T_NAMESPACE;
+use const T_NS_SEPARATOR;
+use const T_STRING;
+use const T_USE;
+use const T_WHITESPACE;
+
+/**
+ * Parses a file for namespaces/use/class declarations.
+ */
+class TokenParser
+{
+    /**
+     * The token list.
+     *
+     * @phpstan-var list<mixed[]>
+     */
+    private $tokens;
+
+    /**
+     * The number of tokens.
+     *
+     * @var int
+     */
+    private $numTokens;
+
+    /**
+     * The current array pointer.
+     *
+     * @var int
+     */
+    private $pointer = 0;
+
+    /**
+     * @param string $contents
+     */
+    public function __construct($contents)
+    {
+        $this->tokens = token_get_all($contents);
+
+        // The PHP parser sets internal compiler globals for certain things. Annoyingly, the last docblock comment it
+        // saw gets stored in doc_comment. When it comes to compile the next thing to be include()d this stored
+        // doc_comment becomes owned by the first thing the compiler sees in the file that it considers might have a
+        // docblock. If the first thing in the file is a class without a doc block this would cause calls to
+        // getDocBlock() on said class to return our long lost doc_comment. Argh.
+        // To workaround, cause the parser to parse an empty docblock. Sure getDocBlock() will return this, but at least
+        // it's harmless to us.
+        token_get_all("<?php\n/**\n *\n */");
+
+        $this->numTokens = count($this->tokens);
+    }
+
+    /**
+     * Gets the next non whitespace and non comment token.
+     *
+     * @param bool $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped.
+     * If FALSE then only whitespace and normal comments are skipped.
+     *
+     * @return mixed[]|string|null The token if exists, null otherwise.
+     */
+    public function next($docCommentIsComment = true)
+    {
+        for ($i = $this->pointer; $i < $this->numTokens; $i++) {
+            $this->pointer++;
+            if (
+                $this->tokens[$i][0] === T_WHITESPACE ||
+                $this->tokens[$i][0] === T_COMMENT ||
+                ($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)
+            ) {
+                continue;
+            }
+
+            return $this->tokens[$i];
+        }
+
+        return null;
+    }
+
+    /**
+     * Parses a single use statement.
+     *
+     * @return array<string, string> A list with all found class names for a use statement.
+     */
+    public function parseUseStatement()
+    {
+        $groupRoot     = '';
+        $class         = '';
+        $alias         = '';
+        $statements    = [];
+        $explicitAlias = false;
+        while (($token = $this->next())) {
+            if (! $explicitAlias && $token[0] === T_STRING) {
+                $class .= $token[1];
+                $alias  = $token[1];
+            } elseif ($explicitAlias && $token[0] === T_STRING) {
+                $alias = $token[1];
+            } elseif (
+                PHP_VERSION_ID >= 80000 &&
+                ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)
+            ) {
+                $class .= $token[1];
+
+                $classSplit = explode('\\', $token[1]);
+                $alias      = $classSplit[count($classSplit) - 1];
+            } elseif ($token[0] === T_NS_SEPARATOR) {
+                $class .= '\\';
+                $alias  = '';
+            } elseif ($token[0] === T_AS) {
+                $explicitAlias = true;
+                $alias         = '';
+            } elseif ($token === ',') {
+                $statements[strtolower($alias)] = $groupRoot . $class;
+                $class                          = '';
+                $alias                          = '';
+                $explicitAlias                  = false;
+            } elseif ($token === ';') {
+                $statements[strtolower($alias)] = $groupRoot . $class;
+                break;
+            } elseif ($token === '{') {
+                $groupRoot = $class;
+                $class     = '';
+            } elseif ($token === '}') {
+                continue;
+            } else {
+                break;
+            }
+        }
+
+        return $statements;
+    }
+
+    /**
+     * Gets all use statements.
+     *
+     * @param string $namespaceName The namespace name of the reflected class.
+     *
+     * @return array<string, string> A list with all found use statements.
+     */
+    public function parseUseStatements($namespaceName)
+    {
+        $statements = [];
+        while (($token = $this->next())) {
+            if ($token[0] === T_USE) {
+                $statements = array_merge($statements, $this->parseUseStatement());
+                continue;
+            }
+
+            if ($token[0] !== T_NAMESPACE || $this->parseNamespace() !== $namespaceName) {
+                continue;
+            }
+
+            // Get fresh array for new namespace. This is to prevent the parser to collect the use statements
+            // for a previous namespace with the same name. This is the case if a namespace is defined twice
+            // or if a namespace with the same name is commented out.
+            $statements = [];
+        }
+
+        return $statements;
+    }
+
+    /**
+     * Gets the namespace.
+     *
+     * @return string The found namespace.
+     */
+    public function parseNamespace()
+    {
+        $name = '';
+        while (
+            ($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR || (
+            PHP_VERSION_ID >= 80000 &&
+            ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)
+            ))
+        ) {
+            $name .= $token[1];
+        }
+
+        return $name;
+    }
+
+    /**
+     * Gets the class name.
+     *
+     * @return string The found class name.
+     */
+    public function parseClass()
+    {
+        // Namespaces and class names are tokenized the same: T_STRINGs
+        // separated by T_NS_SEPARATOR so we can use one function to provide
+        // both.
+        return $this->parseNamespace();
+    }
+}

+ 19 - 0
vendor/doctrine/lexer/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2006-2018 Doctrine Project
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 9 - 0
vendor/doctrine/lexer/README.md

@@ -0,0 +1,9 @@
+# Doctrine Lexer
+
+Build Status: [![Build Status](https://travis-ci.org/doctrine/lexer.svg?branch=master)](https://travis-ci.org/doctrine/lexer)
+
+Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.
+
+This lexer is used in Doctrine Annotations and in Doctrine ORM (DQL).
+
+https://www.doctrine-project.org/projects/lexer.html

+ 41 - 0
vendor/doctrine/lexer/composer.json

@@ -0,0 +1,41 @@
+{
+    "name": "doctrine/lexer",
+    "type": "library",
+    "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
+    "keywords": [
+        "php",
+        "parser",
+        "lexer",
+        "annotations",
+        "docblock"
+    ],
+    "homepage": "https://www.doctrine-project.org/projects/lexer.html",
+    "license": "MIT",
+    "authors": [
+        {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
+        {"name": "Roman Borschel", "email": "roman@code-factory.org"},
+        {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
+    ],
+    "require": {
+        "php": "^7.2 || ^8.0"
+    },
+    "require-dev": {
+        "doctrine/coding-standard": "^6.0",
+        "phpstan/phpstan": "^0.11.8",
+        "phpunit/phpunit": "^8.2"
+    },
+    "autoload": {
+        "psr-4": { "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" }
+    },
+    "autoload-dev": {
+        "psr-4": { "Doctrine\\Tests\\": "tests/Doctrine" }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.2.x-dev"
+        }
+    },
+    "config": {
+        "sort-packages": true
+    }
+}

+ 328 - 0
vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php

@@ -0,0 +1,328 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Common\Lexer;
+
+use ReflectionClass;
+use const PREG_SPLIT_DELIM_CAPTURE;
+use const PREG_SPLIT_NO_EMPTY;
+use const PREG_SPLIT_OFFSET_CAPTURE;
+use function implode;
+use function in_array;
+use function preg_split;
+use function sprintf;
+use function substr;
+
+/**
+ * Base class for writing simple lexers, i.e. for creating small DSLs.
+ */
+abstract class AbstractLexer
+{
+    /**
+     * Lexer original input string.
+     *
+     * @var string
+     */
+    private $input;
+
+    /**
+     * Array of scanned tokens.
+     *
+     * Each token is an associative array containing three items:
+     *  - 'value'    : the string value of the token in the input string
+     *  - 'type'     : the type of the token (identifier, numeric, string, input
+     *                 parameter, none)
+     *  - 'position' : the position of the token in the input string
+     *
+     * @var array
+     */
+    private $tokens = [];
+
+    /**
+     * Current lexer position in input string.
+     *
+     * @var int
+     */
+    private $position = 0;
+
+    /**
+     * Current peek of current lexer position.
+     *
+     * @var int
+     */
+    private $peek = 0;
+
+    /**
+     * The next token in the input.
+     *
+     * @var array|null
+     */
+    public $lookahead;
+
+    /**
+     * The last matched/seen token.
+     *
+     * @var array|null
+     */
+    public $token;
+
+    /**
+     * Composed regex for input parsing.
+     *
+     * @var string
+     */
+    private $regex;
+
+    /**
+     * Sets the input data to be tokenized.
+     *
+     * The Lexer is immediately reset and the new input tokenized.
+     * Any unprocessed tokens from any previous input are lost.
+     *
+     * @param string $input The input to be tokenized.
+     *
+     * @return void
+     */
+    public function setInput($input)
+    {
+        $this->input  = $input;
+        $this->tokens = [];
+
+        $this->reset();
+        $this->scan($input);
+    }
+
+    /**
+     * Resets the lexer.
+     *
+     * @return void
+     */
+    public function reset()
+    {
+        $this->lookahead = null;
+        $this->token     = null;
+        $this->peek      = 0;
+        $this->position  = 0;
+    }
+
+    /**
+     * Resets the peek pointer to 0.
+     *
+     * @return void
+     */
+    public function resetPeek()
+    {
+        $this->peek = 0;
+    }
+
+    /**
+     * Resets the lexer position on the input to the given position.
+     *
+     * @param int $position Position to place the lexical scanner.
+     *
+     * @return void
+     */
+    public function resetPosition($position = 0)
+    {
+        $this->position = $position;
+    }
+
+    /**
+     * Retrieve the original lexer's input until a given position.
+     *
+     * @param int $position
+     *
+     * @return string
+     */
+    public function getInputUntilPosition($position)
+    {
+        return substr($this->input, 0, $position);
+    }
+
+    /**
+     * Checks whether a given token matches the current lookahead.
+     *
+     * @param int|string $token
+     *
+     * @return bool
+     */
+    public function isNextToken($token)
+    {
+        return $this->lookahead !== null && $this->lookahead['type'] === $token;
+    }
+
+    /**
+     * Checks whether any of the given tokens matches the current lookahead.
+     *
+     * @param array $tokens
+     *
+     * @return bool
+     */
+    public function isNextTokenAny(array $tokens)
+    {
+        return $this->lookahead !== null && in_array($this->lookahead['type'], $tokens, true);
+    }
+
+    /**
+     * Moves to the next token in the input string.
+     *
+     * @return bool
+     */
+    public function moveNext()
+    {
+        $this->peek      = 0;
+        $this->token     = $this->lookahead;
+        $this->lookahead = isset($this->tokens[$this->position])
+            ? $this->tokens[$this->position++] : null;
+
+        return $this->lookahead !== null;
+    }
+
+    /**
+     * Tells the lexer to skip input tokens until it sees a token with the given value.
+     *
+     * @param string $type The token type to skip until.
+     *
+     * @return void
+     */
+    public function skipUntil($type)
+    {
+        while ($this->lookahead !== null && $this->lookahead['type'] !== $type) {
+            $this->moveNext();
+        }
+    }
+
+    /**
+     * Checks if given value is identical to the given token.
+     *
+     * @param mixed      $value
+     * @param int|string $token
+     *
+     * @return bool
+     */
+    public function isA($value, $token)
+    {
+        return $this->getType($value) === $token;
+    }
+
+    /**
+     * Moves the lookahead token forward.
+     *
+     * @return array|null The next token or NULL if there are no more tokens ahead.
+     */
+    public function peek()
+    {
+        if (isset($this->tokens[$this->position + $this->peek])) {
+            return $this->tokens[$this->position + $this->peek++];
+        }
+
+        return null;
+    }
+
+    /**
+     * Peeks at the next token, returns it and immediately resets the peek.
+     *
+     * @return array|null The next token or NULL if there are no more tokens ahead.
+     */
+    public function glimpse()
+    {
+        $peek       = $this->peek();
+        $this->peek = 0;
+
+        return $peek;
+    }
+
+    /**
+     * Scans the input string for tokens.
+     *
+     * @param string $input A query string.
+     *
+     * @return void
+     */
+    protected function scan($input)
+    {
+        if (! isset($this->regex)) {
+            $this->regex = sprintf(
+                '/(%s)|%s/%s',
+                implode(')|(', $this->getCatchablePatterns()),
+                implode('|', $this->getNonCatchablePatterns()),
+                $this->getModifiers()
+            );
+        }
+
+        $flags   = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE;
+        $matches = preg_split($this->regex, $input, -1, $flags);
+
+        if ($matches === false) {
+            // Work around https://bugs.php.net/78122
+            $matches = [[$input, 0]];
+        }
+
+        foreach ($matches as $match) {
+            // Must remain before 'value' assignment since it can change content
+            $type = $this->getType($match[0]);
+
+            $this->tokens[] = [
+                'value' => $match[0],
+                'type'  => $type,
+                'position' => $match[1],
+            ];
+        }
+    }
+
+    /**
+     * Gets the literal for a given token.
+     *
+     * @param int|string $token
+     *
+     * @return int|string
+     */
+    public function getLiteral($token)
+    {
+        $className = static::class;
+        $reflClass = new ReflectionClass($className);
+        $constants = $reflClass->getConstants();
+
+        foreach ($constants as $name => $value) {
+            if ($value === $token) {
+                return $className . '::' . $name;
+            }
+        }
+
+        return $token;
+    }
+
+    /**
+     * Regex modifiers
+     *
+     * @return string
+     */
+    protected function getModifiers()
+    {
+        return 'iu';
+    }
+
+    /**
+     * Lexical catchable patterns.
+     *
+     * @return array
+     */
+    abstract protected function getCatchablePatterns();
+
+    /**
+     * Lexical non-catchable patterns.
+     *
+     * @return array
+     */
+    abstract protected function getNonCatchablePatterns();
+
+    /**
+     * Retrieve token type. Also processes the token value if necessary.
+     *
+     * @param string $value
+     *
+     * @return int|string|null
+     */
+    abstract protected function getType(&$value);
+}

+ 17 - 0
vendor/hg/apidoc/.github/workflows/mirror.yml

@@ -0,0 +1,17 @@
+name: 'GitHub Actions Mirror'
+
+on: [push]
+
+jobs:
+  mirror_to_gitee:
+    runs-on: ubuntu-latest
+    steps:
+      - name: 'Checkout'
+        uses: actions/checkout@v1
+      - name: 'Mirror to gitee'
+        uses: pixta-dev/repository-mirroring-action@v1
+        with:
+          target_repo_url:
+            git@gitee.com:hg-code/thinkphp-apidoc.git
+          ssh_private_key:
+            ${{ secrets.GITEE_KEY }}

+ 1 - 0
vendor/hg/apidoc/.gitignore

@@ -0,0 +1 @@
+.idea

+ 21 - 0
vendor/hg/apidoc/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 HG
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 78 - 0
vendor/hg/apidoc/README.md

@@ -0,0 +1,78 @@
+<p align="center">
+    <img width="120" src="https://apidoc.demo.hg-code.com/images/logo.png">
+</p>
+
+<h1 align="center">
+  ThinkPHP ApiDoc
+</h1>
+
+<div align="center">
+基于ThinkPHP 根据注释自动生成API接口文档
+</div>
+
+<div align="center" style="margin-top:10px;margin-bottom:50px;">
+<a href="https://packagist.org/packages/hg/apidoc"><img src="https://img.shields.io/packagist/v/hg/apidoc"></a>
+<a href="https://packagist.org/packages/hg/apidoc"><img src="https://img.shields.io/packagist/dt/hg/apidoc"></a>
+<a href="https://packagist.org/packages/hg/apidoc"><img src="https://img.shields.io/packagist/l/hg/apidoc"></a>
+<a href="https://github.com/HGthecode/thinkphp-apidoc"><img src="https://img.shields.io/github/issues/HGthecode/thinkphp-apidoc"></a>
+<a href="https://github.com/HGthecode/thinkphp-apidoc"><img src="https://img.shields.io/github/forks/HGthecode/thinkphp-apidoc"></a>
+
+</div>
+
+
+## 🤷‍♀️ Apidoc是什么?
+
+如今,前后端分离的开发模式以必不可少,基于ThinkPHP你可以很方便的作为后端Api的开发。可是一个Api开发过程中需要快速调试,开发完成后需要给其它开发者对接,这时一个详细的Api文档,就显得特别重要。
+
+大多数开发者可能都是通过各种工具配合来达到这一目的,其各种工具的安装和配置也是繁琐。甚至还有通过word等文本工具手写api文档的,这样的开发效率与可维护性是非常差的。
+
+综合种种Api开发中的痛点,我们专为ThinkPHP开发了Apidoc的扩展插件,本插件可通过简单的注解即可生成Api文档,及帮助开发者提高生产效率的在线调试、快速生成Crud、一键生成整个模块Api等,涵盖Api开发方方面面。
+
+
+## ✨特性
+
+- 开箱即用:无繁杂的配置、安装后按文档编写注释即可自动生成API文档。
+- 在线调试:在线文档可直接调试,支持设置全局参数,接口调试省时省力。
+- 轻松编写:支持公共注释定义、业务逻辑层、数据表字段的引用,几句注释即可完成。
+- 安全高效:访问API文档页面需密码验证;支持缓存接口数据,无需每次都生成数据。
+- 多应用/多版本:可适应各种单应用、多应用、多版本的项目的Api管理。
+- Markdown文档:支持.md文件的文档展示。
+- 快速生成Crud:配置+模板即可快速生成Crud接口代码及数据表的创建,大大提高工作效率。
+- 控制器分组:更精细化的对控制器接口进行分组展示。
+
+
+## 📌兼容
+
+ThinkPHP 6.x
+
+> 如需在 `ThinkPHP 5.x` 版本中使用,请安装 `V1.x`版本 [V1.x版本安装说明](https://hgthecode.github.io/thinkphp-apidoc/v1/install/)
+
+
+## 📖使用文档
+
+[ThinkPHP ApiDoc V2.x文档](https://hg-code.gitee.io/thinkphp-apidoc/)
+
+[ThinkPHP ApiDoc V1.x文档](https://hg-code.gitee.io/thinkphp-apidoc/v1/)
+
+## 🏆支持我们
+
+如果本项目对您有所帮助,请点个Star支持我们
+
+- [Github](https://github.com/HGthecode/thinkphp-apidoc) -> <a href="https://github.com/HGthecode/thinkphp-apidoc" target="_blank">
+  <img height="22" src="https://img.shields.io/github/stars/HGthecode/thinkphp-apidoc?style=social" class="attachment-full size-full" alt="Star me on GitHub" data-recalc-dims="1" /></a>
+- [Gitee](https://gitee.com/hg-code/thinkphp-apidoc) -> <a href="https://gitee.com/hg-code/thinkphp-apidoc/stargazers"><img src="https://gitee.com/hg-code/thinkphp-apidoc/badge/star.svg" alt="star"></a>
+
+## 💡鸣谢
+
+[ThinkPHP](http://www.thinkphp.cn/)
+<a href="http://www.thinkphp.cn/" target="_blank">ThinkPHP</a>
+
+<a href="https://github.com/doctrine/annotations" target="_blank">doctrine/annotations</a>
+
+
+## 🔗链接
+ <a href="https://github.com/HGthecode/apidoc-ui" target="_blank">ApiDoc UI前端</a>
+ 
+ <a href="https://github.com/HGthecode/thinkphp-apidoc-demo" target="_blank">ApiDoc Demo 示例项目</a>
+
+

+ 42 - 0
vendor/hg/apidoc/composer.json

@@ -0,0 +1,42 @@
+{
+    "name": "hg/apidoc",
+    "description": "thinkphp API文档自动生成",
+    "type": "think-extend",
+    "keywords": [
+        "thinkphp",
+        "swagger",
+        "apidoc",
+        "api文档",
+        "接口文档",
+        "自动生成",
+        "注释生成",
+        "php接口文档"
+      ],
+    "require": {
+        "doctrine/annotations": "^1.6",
+        "symfony/class-loader": "~3.2.0"
+    },
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "hg-code",
+            "email": "376401263@qq.com"
+        }
+    ],
+    "autoload": {
+        "psr-4": {
+            "hg\\apidoc\\": "src/"
+        }
+    },
+    "extra": {
+        "think": {
+            "services": [
+                "hg\\apidoc\\Service"
+            ],
+            "config": {
+                "apidoc": "src/config.php"
+            }
+        }
+    },
+    "minimum-stability": "beta"
+}

+ 44 - 0
vendor/hg/apidoc/src/Auth.php

@@ -0,0 +1,44 @@
+<?php
+namespace hg\apidoc;
+
+use think\App;
+use think\facade\Request;
+
+class Auth
+{
+    protected $config=[];
+
+    public function __construct($config)
+    {
+        $this->config = $config;
+    }
+
+    /**
+     * 验证密码
+     * @param $password
+     * @return false|string
+     */
+    public function verifyAuth($password){
+        if ($this->config['auth']['enable']){
+            // 密码验证
+            if (md5($this->config['auth']['password']) === $password){
+                $token = md5(md5($this->config['auth']['password']).$this->config['auth']['secret_key'].strtotime(date('Y-m-d',time())));
+                return $token;
+            }
+            throw new \think\Exception("密码不正确,请重新输入");
+        }
+        return false;
+    }
+
+    /**
+     * 验证token
+     * @param $token
+     * @return bool
+     */
+    public function checkToken($token){
+        if ($token === md5(md5($this->config['auth']['password']).$this->config['auth']['secret_key'].strtotime(date('Y-m-d',time())))){
+            return true;
+        }
+        return false;
+    }
+}

+ 475 - 0
vendor/hg/apidoc/src/Controller.php

@@ -0,0 +1,475 @@
+<?php
+
+
+namespace hg\apidoc;
+
+use think\facade\Request;
+use think\App;
+use Doctrine\Common\Annotations\Reader;
+use Symfony\Component\ClassLoader\ClassMapGenerator;
+use think\facade\Cache;
+
+
+
+class Controller
+{
+    use ParseAnnotation,ParseMarkdown;
+
+    /**
+     * @var App 
+     */
+    protected $app;
+
+    /**
+     * @var Reader
+     */
+    protected $reader;
+
+    // 当前应用的所有节点
+    protected $currentApps;
+    // 当前应用
+    protected $currentApp;
+
+    //tags,当前应用/版本所有的tag
+    protected $tags=array();
+
+    //groups,当前应用/版本的分组name
+    protected $groups=array();
+
+    protected $defaultConfig=[
+        'crud'=>[
+            'model'=>[
+                'fields_types'=>[
+                    "int",
+                    "tinyint",
+                    "smallint",
+                    "mediumint",
+                    "integer",
+                    "bigint",
+                    "bit",
+                    "real",
+                    "float",
+                    "decimal",
+                    "numeric",
+                    "char",
+                    "varchar",
+                    "date",
+                    "time",
+                    "year",
+                    "timestamp",
+                    "datetime",
+                    "tinyblob",
+                    "blob",
+                    "mediumblob",
+                    "longblob",
+                    "tinytext",
+                    "text",
+                    "mediumtext",
+                    "longtext",
+                    "enum",
+                    "set",
+                    "binary",
+                    "varbinary",
+                    "point",
+                    "linestring",
+                    "polygon",
+                    "geometry",
+                    "multipoint",
+                    "multilinestring",
+                    "multipolygon",
+                    "geometrycollection"
+                ]
+            ]
+        ]
+    ];
+
+    protected $config;
+
+    
+    public function __construct(App $app,Reader $reader)
+    {
+        $this->app = $app;
+        $this->reader = $reader;
+        $this->config = $app->config->get('apidoc');
+    }
+
+    /**
+     * 获取配置
+     * @return array
+     */
+    public function getConfig(){
+        $config = $this->config;
+        if (!empty($config['auth'])){
+            unset($config['auth']['auth_password']);
+            unset($config['auth']['password']);
+            unset($config['auth']['key']);
+        }
+        // 处理统一返回信息
+        if (!empty($config['responses']) && is_string($config['responses'])){
+            // 兼容原配置
+            $config['responses'] = [
+                'jsonStr'=>$config['responses']
+            ];
+        }else if (!empty($config['responses']) && isset($config['responses']['show_responses']) && !$config['responses']['show_responses'] && !empty($config['responses']['data'])){
+            // 显示在提示中
+            $responsesStr = '{'."\r\n";
+            $responsesMain = "";
+            foreach ($config['responses']['data'] as $item){
+                $responsesStr.='"'.$item['name'].'":"'.$item['desc'].'",'."\r\n";
+                if (!empty($item['main']) && $item['main']==true){
+                    $responsesMain = $item;
+                }
+            }
+            $responsesStr.= '}';
+            $config['responses']['jsonStr']=$responsesStr;
+            $config['responses']['main']=$responsesMain;
+        }
+        $config['debug']=$this->app->isDebug();
+
+
+        if (!empty($config['crud'])){
+            // 无配置可选字段类型,使用默认的
+            if (!empty($config['crud']['model']) && empty($config['crud']['model']['fields_types'])){
+                $config['crud']['model']['fields_types'] = $this->defaultConfig['crud']['model']['fields_types'];
+            }
+            // 过滤route文件配置
+            if (!empty($config['crud']['route'])){
+                unset($config['crud']['route']);
+            }
+        }
+
+        return $this->showJson(0,"",$config);
+    }
+
+
+
+    /**
+     * 验证Token
+     */
+    public function checkAuth(){
+        $config = $this->config;
+        if (!(!empty($config['auth']) && $config['auth']['enable'])) {
+            return true;
+        }
+        $request = Request::instance();
+        $headers_key = !empty($config['auth']) && !empty($config['auth']['headers_key'])?$config['auth']['headers_key']:"apidocToken";
+        $token = $request->header($headers_key);
+        if (empty($token)){
+            throw new \think\exception\HttpException(401, "无token");
+        }
+        $hasAuth = (new Auth($config))->checkToken($token);
+        return $hasAuth;
+    }
+
+    /**
+     * 登录,密码验证
+     */
+    public function verifyAuth(){
+        $config = $this->config;
+        if (!(!empty($config['auth']) && $config['auth']['enable'])) {
+            return false;
+        }
+        $request = Request::instance();
+        $params = $request->param();
+        $password = $params['password'];
+        if (empty($password)){
+            throw new \think\exception\HttpException(415, "请输入密码");
+        }
+        $hasAuth = (new Auth($config))->verifyAuth($password);
+        return $this->showJson(0,"",$hasAuth);
+    }
+
+    /**
+     * 统一返回json
+     * @param int $code
+     * @param string $msg
+     * @param string $data
+     * @return \think\response\Json
+     */
+    private function showJson($code=0,$msg="",$data=""){
+        $res=[
+            'code'=>$code,
+            'msg'=>$msg,
+            'data'=>$data,
+        ];
+        return json($res);
+    }
+
+    /**
+     * 创建Crud
+     * @return \think\response\Json
+     */
+    public function createCrud(){
+        if (!$this->app->isDebug()){
+            throw new \think\exception\HttpException(415, "请在debug模式下,使用该功能");
+        }
+
+        $config = $this->config;
+        $request = Request::instance();
+        $params = $request->param();
+        $this->initCurrentApps($params['appKey']);
+        $res = (new CreateCrud($config,$this->currentApps,$this->app))->create($params);
+        return $this->showJson(0,"",$res);
+    }
+
+
+    /**
+     * 初始化当前所选的应用/版本数据
+     * @param $appKey
+     */
+    protected function initCurrentApps($appKey){
+        $config = $this->config;
+        if (strpos($appKey,'_')!==false){
+            $keyArr = explode("_", $appKey);
+        }else{
+            $keyArr =[$appKey];
+        }
+        $this->currentApps = (new Utils())->getTreeNodesByKeys($config['apps'],$keyArr,'folder','items');
+        $this->currentApp = $this->currentApps[count($this->currentApps)-1];
+    }
+
+    /**
+     * 获取文档数据
+     * @return \think\response\Json
+     */
+    public function getData(){
+        $config = $this->config;
+        // 验证token身份
+        if ($config['auth']['enable']){
+            $tokenRes = $this->checkAuth();
+            if (!$tokenRes){
+                throw new \think\exception\HttpException(401, "token失效");
+            }
+        }
+
+        $request = Request::instance();
+        $params = $request->param();
+        if (!empty($params) && !empty($params['appKey'])){
+           $this->initCurrentApps($params['appKey']);
+        }
+        if ($config['cache']['enable']){
+            // 获取缓存数据
+            $path = !empty($config['cache']) && !empty($config['cache']['path'])?$config['cache']['path']:'../runtime/apidoc/';
+            $cacheAppPath="";
+            if (!empty($this->currentApps) && count($this->currentApps)>0){
+                foreach ($this->currentApps as $keyIndex=>$appItem){
+                    $cacheAppPath.=$appItem['folder']."/";
+                }
+            }
+            $cachePath = $path.$cacheAppPath;
+            $cacheFiles= [];
+            $cacheName="";
+            $filePaths=glob($cachePath.'/*.json');
+            if (file_exists($cachePath) && $params['reload']=='false' ){
+                $cacheFilePath = "";
+                if (count($filePaths)>0){
+                    $cacheFilePath =  $filePaths[count($filePaths)-1];
+                }
+                if (!empty($params) && !empty($params['cacheFileName'])){
+                    // 前端传入的缓存文件名
+                    $cacheFileName = $params['cacheFileName'];
+                    $cacheFilePath = $cachePath."/".$cacheFileName.'.json';
+                }
+                if ($cacheFilePath && file_exists($cacheFilePath)){
+                    $fileContent= file_get_contents($cacheFilePath);
+                    if (!empty($fileContent)){
+                        $fileJson = json_decode($fileContent);
+                        $json = $fileJson;
+                        $cacheName=str_replace(".json","",basename($cacheFilePath));
+                    }else{
+                        $json = $this->reloadData($params);
+                    }
+                }else{
+                    // 不存在缓存文件,生成数据并存缓存
+                    $json = $this->reloadData($params);
+                    $cacheName=$this->createCacheFile($json);
+                    if ($config['cache']['max'] && count($filePaths)>=$config['cache']['max']){
+                        //达到最大数量,删除第一个
+                        Utils::delFile($filePaths[0]);
+                    }
+                }
+
+            }else{
+                // 不存在缓存文件,生成数据并存缓存
+                $json = $this->reloadData($params);
+                $cacheName=$this->createCacheFile($json);
+                if ($config['cache']['max'] && count($filePaths)>=$config['cache']['max']){
+                    //达到最大数量,删除第一个
+                    Utils::delFile($filePaths[0]);
+                }
+            }
+            $filePaths=glob($cachePath.'/*.json');
+            if (count($filePaths)>0){
+                foreach($filePaths as $item)
+                {
+                    $cacheFiles[]=str_replace(".json","",basename($item));
+                }
+            }
+            if (is_array($json)){
+                $json['cacheFiles']=$cacheFiles;
+                $json['cacheName']=$cacheName;
+            }else{
+                $json->cacheFiles=$cacheFiles;
+                $json->cacheName=$cacheName;
+            }
+
+
+        }else{
+            $json = $this->reloadData($params);
+        }
+
+        return $this->showJson(0,"",$json);
+    }
+
+
+    /**
+     * 生成文档数据
+     * @param $params
+     * @return array
+     */
+    public function reloadData($params)
+    {
+        $config = $this->config;
+        $apiData = $this->renderApiData();
+
+        $groups=[['title'=>'全部','name'=>0]];
+        // 获取md
+        $docs=[];
+        if (!empty($config['docs']) && !empty($config['docs']['menus']) && count($config['docs']['menus'])>0){
+            $docs = $this->renderDocs($config['docs']['menus']);
+            $menu_title = !empty($config['docs']) && !empty($config['docs']['menu_title'])?$config['docs']['menu_title']:'文档';
+            $groups[]=['title'=>$menu_title,'name'=>'markdown_doc'];
+        }
+        if (!empty($config['groups']) && count($config['groups'])>0 && !empty($this->groups) && count($this->groups)>0){
+            $configGroups=[];
+            foreach ($config['groups'] as $group) {
+                if (in_array($group['name'],$this->groups)){
+                    $configGroups[]=$group;
+                }
+            }
+            $groups = array_merge($groups,$configGroups);
+        }
+
+        $json=[
+            'groups'=>$groups,
+            'list'=>$apiData,
+            'docs'=>$docs,
+            'tags'=>$this->tags
+        ];
+        return $json;
+    }
+
+    /**
+     * 获取生成文档的控制器
+     * @param $path
+     * @return array
+     */
+    protected function getConfigControllers($path){
+        $config = $this->config;
+        $controllers=[];
+
+        $configControllers = $config['controllers'];
+        if (!empty($configControllers) && count($configControllers)>0){
+            foreach ($configControllers as $item){
+                $itemPath = $item;
+                $class = $path.'\\'. $itemPath;
+                if (class_exists($class)) {
+                    $controllers[]=$class;
+                }
+            }
+        }
+        return $controllers;
+    }
+
+    /**
+     * 获取目录下的控制器
+     * @param $path
+     * @return array
+     */
+    protected function getDirControllers($path){
+        if ($path){
+            $pathStr = str_replace("\\","/",$path);
+            $dir = $this->app->getRootPath() . $pathStr;
+        }else{
+            $dir = $this->app->getRootPath() . $this->app->config->get('route.controller_layer');
+        }
+        $controllers=[];
+        if (is_dir($dir)) {
+            $controllers =$this->scanDir($dir,$path);
+        }
+        return $controllers;
+    }
+
+    /**
+     * 处理目录下的控制器
+     * @param $dir
+     * @param $appPath
+     * @return array
+     */
+    protected function scanDir($dir,$appPath)
+    {
+        $list=[];
+        foreach (ClassMapGenerator::createMap($dir) as $class => $path) {
+            if (
+                !isset($this->config['filter_controllers']) ||
+                (isset($this->config['filter_controllers']) &&  !in_array($class,$this->config['filter_controllers'])) &&
+                $this->config['definitions'] != $class
+            ){
+                $list[] = $class;
+            }
+        }
+        return $list;
+    }
+
+    /**
+     * 生成api接口数据
+     * @return array
+     */
+    public function renderApiData(){
+        $config = $this->config;
+        $apiData=[];
+        $path = $this->currentApp['path'];
+        if (!empty($config['controllers']) && count($config['controllers'])>0){
+            // 配置的控制器列表
+            $controllers = $this->getConfigControllers($path);
+        }else{
+            // 默认读取所有的
+            $controllers = $this->getDirControllers($path);
+        }
+
+        foreach ($controllers as $class){
+            $classData=$this->parseController($class);
+            if ($classData!==false){
+                $apiData[] = $classData;
+            }
+        }
+        return $apiData;
+    }
+
+    /**
+     * 创建接口参数缓存文件
+     * @param $json
+     * @return bool|false|string
+     */
+    protected function createCacheFile($json){
+        if (empty($json)){
+            return false;
+        }
+        $config = $this->config;
+        $path = !empty($config['cache']) && !empty($config['cache']['path'])?$config['cache']['path']:'../runtime/apidoc/';
+        $fileName =date("Y-m-d H_i_s");
+        $fileJson = $json;
+        $fileContent = json_encode($fileJson);
+        $dir = $path;
+        if (!empty($this->currentApps) && count($this->currentApps)>0){
+            foreach ($this->currentApps as $appItem){
+                $dir.="/".$appItem['folder'];
+            }
+        }
+        $path = $dir."/".$fileName.".json";
+        Utils::createFile($path,$fileContent);
+        return $fileName;
+    }
+
+
+}

+ 317 - 0
vendor/hg/apidoc/src/CreateCrud.php

@@ -0,0 +1,317 @@
+<?php
+
+
+namespace hg\apidoc;
+
+
+use think\facade\Config;
+use think\facade\Db;
+use think\App;
+use think\helper\Str;
+
+
+class CreateCrud
+{
+    protected $app;
+
+    protected $config;
+
+    protected $currentApps;
+
+    public function __construct($config,$currentApps,App $app)
+    {
+        $this->config = $config['crud'];
+        $this->currentApps = $currentApps;
+        $this->app = $app;
+    }
+
+    /**
+     * 创建Crud
+     * @param $params
+     * @return array
+     */
+    public function create($params){
+
+        $data = $this->renderTemplateData($params);
+        $res = [];
+        // 创建数据表
+        if (!empty($params['model']['table'])){
+            $sqlRes = $this->createModelTable($params['model'],$params['title']);
+            if ($sqlRes == true){
+                $res[]="创建数据表成功";
+            }else{
+                $msg= $sqlRes?$sqlRes:"数据表创建失败,请检查配置";
+                throw new \think\exception\HttpException(500, $msg);
+            }
+        }
+        // 生成文件
+        $keys = array_keys($this->config);
+        foreach ($keys as $index=>$key) {
+            // 添加路由
+            if (
+                $key==="route" &&
+                !empty($this->config['route']) &&
+                !empty($this->config['route']['template']) &&
+                !empty($this->config['route']['path'])
+            ){
+                $tmp_path = (new Utils())->replaceCurrentAppTemplate($this->config['route']['template'],$this->currentApps);
+                $tempPath = $tmp_path.".txt";
+                $str_tmp = Utils::getFileContent($tempPath);
+                if (!empty($str_tmp)){
+                    $tmp_content = Utils::replaceTemplate($str_tmp,$data);
+                    $tmp_content = (new Utils())->replaceCurrentAppTemplate($tmp_content,$this->currentApps);
+                    $routePathStr = (new Utils())->replaceCurrentAppTemplate($this->config['route']['path'],$this->currentApps);
+                    $routePathStr = str_replace("\\","/",$routePathStr);
+                    $routePath = $this->app->getAppPath().$routePathStr;
+                    $routeContent = Utils::getFileContent($routePath);
+                    $routeContent.="\r\n".$tmp_content."\r\n";
+                    Utils::createFile($routePath,$routeContent);
+                    $res[]="添加路由成功";
+                }
+
+            }else{
+                $currentParam = $params[$key];
+                $tmp_path = (new Utils())->replaceCurrentAppTemplate($currentParam['template'],$this->currentApps);
+                $tempPath = $tmp_path.".txt";
+                $str_tmp = Utils::getFileContent($tempPath);
+                $file_content = Utils::replaceTemplate($str_tmp,$data);
+                $file_content = (new Utils())->replaceCurrentAppTemplate($file_content,$this->currentApps);
+                $namespacePath = str_replace("\\","/",$currentParam['path']);
+                $filePath = '../'.$namespacePath.'/'.$currentParam['class_name'].'.php';
+                $fp=Utils::createFile($filePath,$file_content);
+                if ($fp){
+                    $res[]="创建文件成功 path:".$filePath;
+                }
+            }
+        }
+
+        return $res;
+    }
+
+
+    /**
+     * 生成模板变量的数据
+     * @param $params
+     * @return array
+     */
+    public function renderTemplateData($params){
+        // 模板参数
+        $api_group = "";
+        if (!empty($params['group'])){
+            $api_group = '@Apidoc\Group("'.$params['group'].'")';
+        }
+        $data=[
+            'title'=>!empty($params['title'])?$params['title']:"",
+            'api_group'=>$api_group,
+        ];
+        $keys = array_keys($this->config);
+        foreach ($keys as $index=>$key){
+            $currentConfig = $this->config[$key];
+            //验证模板是否存在
+            $tmp_path = (new Utils())->replaceCurrentAppTemplate($currentConfig['template'],$this->currentApps);
+            if(!file_exists($tmp_path.'.txt')){
+                throw new \think\exception\HttpException(404, $currentConfig['template']."模板不存在");
+            }
+            if ($key==="route"){
+                continue;
+            }
+            $currentParam = $params[$key];
+            if(!preg_match("/^[A-Z]{1}[A-Za-z0-9]{1,32}$/",$currentParam['class_name'])){
+                throw new \think\exception\HttpException(500, $currentParam['class_name'].'文件名不合法');
+            }
+            $currentParamPath = str_replace("\\","/",$currentParam['path']);
+            // 验证目录是否存在
+            if(!is_dir('../'.$currentParamPath)){
+                throw new \think\exception\HttpException(404, $currentParamPath."目录不存在");
+            }
+
+            if ($key==="controller"){
+                $pathArr = explode("\\", $currentParam['path']);
+                $notArr = array("app", "controller");
+                $url = "/";
+                foreach ($pathArr as $pathItem){
+                    if (!in_array($pathItem,$notArr)){
+                        $url.=$pathItem."/";
+                    }
+                }
+                $data['folder']=$url;
+                $data['api_class_name']=lcfirst($currentParam['class_name']);
+                $data['api_url']=$url.lcfirst($currentParam['class_name']);
+            }else if ($key==="model" && !empty($currentParam['table'])){
+                // 模型
+                // 获取主键
+                foreach ($currentParam['table'] as $item){
+                    if ($item['main_key']==true){
+                        $data['main_key.field'] = $item['field'];
+                        $data['main_key.type'] = $item['type'];
+                        $data['main_key.desc'] = $item['desc'];
+                        break;
+                    }
+                }
+            }
+            $data[$currentParam['name'].'.class_name']=$currentParam['class_name'];
+            $data[$currentParam['name'].'.namespace']=$currentParam['path'];
+            $data[$currentParam['name'].'.use_path']=$currentParam['path']."\\".$currentParam['class_name'];
+            $data[$currentParam['name'].'.use_alias']=$currentParam['class_name'].ucwords($currentParam['name']);
+        }
+
+        // 字段过滤数据
+        if (!empty($params['model']['table'])){
+            $checkKeys = ['list','detail','add','edit'];
+            foreach ($checkKeys as $checkKey){
+                $itemArr = ['field'=>[],'withoutField'=>[]];
+                foreach ($params['model']['table'] as $item){
+                    if ($item[$checkKey]){
+                        $itemArr['field'][]=$item['field'];
+                    }else{
+                        $itemArr['withoutField'][]=$item['field'];
+                    }
+                }
+                $data[$checkKey.'.field']=implode(",", $itemArr['field']);
+                $data[$checkKey.'.withoutField']=implode(",", $itemArr['withoutField']);
+            }
+            // 查询条件、验证规则
+            $query_where='$where=[];'."\r\n";
+            $query_annotation = "";
+            $validate_rule = "["."\r\n";
+            $validate_message = "["."\r\n";
+            $validate_fields = [];
+            $addRequireFields = [];
+            foreach ($params['model']['table'] as $i=>$item){
+                if ($item['query']){
+                    $itemArr['field'][]=$item['field'];
+                    $query_where.= '        if(!empty($param[\''.$item['field'].'\'])){'."\r\n";
+                    $query_where.= '            $where[] = [\''.$item['field'].'\',\'=\',$param[\''.$item['field'].'\']];'."\r\n";
+                    $query_where.= '        }'."\r\n";
+                    $fh = empty($query_annotation)?'* ':'     * ';
+                    $require = $item['not_null']==true?'true':'false';
+                    $rn="";
+                    if (($i+1)<count($params['model']['table'])){
+                        $rn="\r\n";
+                    }
+                    $query_annotation.=$fh.'@Apidoc\Param("'.$item['field'].'",type="'.$item['type'].'",require='.$require.',desc="'.$item['desc'].'")'.$rn;
+                }
+                // 验证规则
+                if (!empty($this->config['validate'])){
+                    // 存在配置验证规则
+                    if (!empty($item['validate']) && $this->config['validate']['rules']){
+                        $rulesConfig = $this->config['validate']['rules'];
+                        $currentRuleConfig = "";
+                        foreach ($rulesConfig as $rule){
+                            if ($rule['rule'] === $item['validate']){
+                                $currentRuleConfig = $rule;
+                                break;
+                            }
+                        }
+                        if (!empty($currentRuleConfig)){
+                            $validate_rule.='       \''.$item['field'].'\' => \''.$item['validate'].'\','."\r\n";
+                            if (!empty($currentRuleConfig['message']) ){
+                                if (is_array($currentRuleConfig['message']) && count($currentRuleConfig['message'])>0){
+                                    foreach ($currentRuleConfig['message'] as $ruleKey=>$ruleMessage){
+                                        if ($ruleKey=='0'){
+                                            $ruleKeyStr = $item['field'];
+                                        }else{
+                                            $ruleKeyStr = Utils::replaceTemplate($ruleKey,$item);
+                                        }
+                                        $ruleMessageStr = Utils::replaceTemplate($ruleMessage,$item);
+                                        $validate_message.='        \''.$ruleKeyStr.'\' => \''.$ruleMessageStr.'\','."\r\n";
+                                    }
+                                }else{
+                                    $ruleMessageStr = Utils::replaceTemplate($currentRuleConfig['message'],$item);
+                                    $validate_message.='        \''.$item['field'].'\' => \''.$ruleMessageStr.'\','."\r\n";
+                                }
+                            }
+                            $validate_fields[]=$item['field'];
+                            if($item['field'] !== $data['main_key.field']){
+                                $addRequireFields[]=$item['field'];
+                            }
+                        }
+                    }else if($item['not_null']){
+                        $validate_fields[]=$item['field'];
+                        if($item['field'] !== $data['main_key.field']){
+                            $addRequireFields[]=$item['field'];
+                        }
+                        // 根据not_null生成必填
+                        $validate_rule.='       \''.$item['field'].'\' => \'require\','."\r\n";
+                        $validate_message.='        \''.$item['field'].'\' => \''.$item['field'].'不可为空\','."\r\n";
+                    }
+                }
+            }
+            $validate_rule.='   ];'."\r\n";
+            $validate_message.='    ];'."\r\n";
+            if (!empty($this->config['validate'])) {
+                $data['validate.rule'] = $validate_rule;
+                $data['validate.message'] = $validate_message;
+                $data['validate.scene.edit'] = json_encode($validate_fields);
+                $data['validate.scene.add'] =  json_encode($addRequireFields)=='[]'?'[\'\']':json_encode($addRequireFields);
+                $data['validate.scene.delete'] = '[\'' . $data['main_key.field'] . '\']';
+            }
+
+            $data['query.where']=$query_where;
+            $data['query.annotation']=$query_annotation;
+        }
+        return $data;
+
+    }
+
+
+    /**
+     * 创建数据表
+     * @return mixed
+     */
+    public function createModelTable($params,$title=""){
+        $data = $params['table'];
+        if (empty($title)){
+            $title =$params['class_name'];
+        }
+        $driver = Config::get('database.default');
+        $table_prefix=Config::get('database.connections.'.$driver.'.prefix');
+//        $table_name =$table_prefix.strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $params['class_name']));
+        $table_name = $table_prefix.Str::snake($params['class_name']);
+
+        $table_data = '';
+        $main_keys = '';
+        foreach ($data as $item){
+
+            $table_field="".$item['field']." ".$item['type']."(".$item['length'].")";
+
+            if ($item['main_key']){
+                $main_keys.=$item['field'];
+                $table_field.=" NOT NULL";
+            }else if ($item['not_null']){
+                $table_field.=" NOT NULL";
+            }
+            if ($item['incremental']==true){
+                $table_field.=" AUTO_INCREMENT";
+            }
+            if (!empty($item['default'])){
+                $table_field.=" DEFAULT '".$item['default']."'";
+            }else if (!$item['main_key'] && !$item['not_null']){
+                $table_field.=" DEFAULT NULL";
+            }
+            $table_field.=" COMMENT '".$item['desc']."',";
+            $table_data.=$table_field;
+        }
+
+        $sql = "CREATE TABLE IF NOT EXISTS `$table_name` (
+        $table_data
+        PRIMARY KEY (`$main_keys`)
+        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='$title' AUTO_INCREMENT=1 ;";
+
+        Db::startTrans();
+        try {
+            Db::query($sql);
+            // 提交事务
+            Db::commit();
+            return true;
+        } catch (\Exception $e) {
+            // 回滚事务
+            Db::rollback();
+            return $e->getMessage();
+        }
+
+    }
+
+
+
+}

+ 457 - 0
vendor/hg/apidoc/src/ParseAnnotation.php

@@ -0,0 +1,457 @@
+<?php
+namespace hg\apidoc;
+
+
+use ReflectionClass;
+use think\annotation\route\Resource;
+use think\annotation\Route;
+use hg\apidoc\annotation\Group;
+use hg\apidoc\annotation\Param;
+use hg\apidoc\annotation\Title;
+use hg\apidoc\annotation\Desc;
+use hg\apidoc\annotation\Author;
+use hg\apidoc\annotation\Tag;
+use hg\apidoc\annotation\Header;
+use hg\apidoc\annotation\Returned;
+use hg\apidoc\annotation\ParamType;
+use hg\apidoc\annotation\Url;
+use hg\apidoc\annotation\Method;
+use think\annotation\route\Group as RouteGroup;
+
+trait ParseAnnotation
+{
+
+    protected function parseController($class)
+    {
+        $config = $this->config;
+        $data=[];
+        $refClass = new ReflectionClass($class);
+        $classTextAnnotations = $this->parseTextAnnotation($refClass);
+        if (in_array("NotParse",$classTextAnnotations)){
+            return false;
+        }
+        $title = $this->reader->getClassAnnotation($refClass,Title::class);
+        $group = $this->reader->getClassAnnotation($refClass,Group::class);
+
+        $routeGroup = $this->reader->getClassAnnotation($refClass,RouteGroup::class);
+        $controllersNameArr = explode("\\", $class);
+        $controllersName = $controllersNameArr[count($controllersNameArr)-1];
+        $data['controller']=$controllersName;
+        $data['group'] = !empty($group->value)?$group->value:null;
+        if (!empty($data['group']) && !in_array($data['group'],$this->groups)){
+            $this->groups[]=$data['group'];
+        }
+        $data['title'] = !empty($title) && !empty($title->value) ? $title->value : "";
+
+        if (empty($title)){
+            if (!empty($classTextAnnotations) && count($classTextAnnotations)>0){
+                $data['title'] = $classTextAnnotations[0];
+            }else{
+                $data['title'] = $controllersName;
+            }
+        }
+        $methodList = [];
+        $filter_method = !empty($config['filter_method'])?$config['filter_method']:[];
+        $data['menu_key']=$data['controller']."_".mt_rand(10000,99999);
+
+        foreach ($refClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $refMethod) {
+            if (!empty($refMethod->name) && !in_array($refMethod->name, $filter_method)){
+                $methodItem= $this->parseAnnotation($refMethod,true);
+                if (!count((array)$methodItem)) {
+                    continue;
+                }
+                $textAnnotations=$this->parseTextAnnotation($refMethod);
+                // 标注不解析的方法
+                if (in_array("NotParse",$textAnnotations)){
+                    continue;
+                }
+                // 无标题,且有文本注释
+                if (empty($methodItem['title']) && !empty($textAnnotations) && count($textAnnotations)>0){
+                    $methodItem['title'] = $textAnnotations[0];
+                }
+                // 添加统一headers请求头参数
+                if (!empty($config['headers']) && !in_array("NotHeaders",$textAnnotations)){
+                    if (!empty($methodItem['header'])) {
+                        $methodItem['header'] = Utils::arrayMergeAndUnique("name",$config['headers'], $methodItem['header']);
+                    }else{
+                        $methodItem['header'] = $config['headers'];
+                    }
+                }
+                // 添加统一params请求参数
+                if (!empty($config['parameters']) && !in_array("NotParameters",$textAnnotations)){
+                    if (!empty($methodItem['param'])) {
+                        $methodItem['param'] = Utils::arrayMergeAndUnique("name",$config['parameters'], $methodItem['param']);
+                    }else{
+                        $methodItem['param'] = $config['parameters'];
+                    }
+                }
+                // 添加responses统一响应体
+                if (
+                    !empty($config['responses']) &&
+                    !is_string($config['responses']) &&
+                    !in_array("NotResponses",$textAnnotations)
+                ){
+                    // 显示在响应体中
+                    $returned = [];
+                    $hasMian=false;
+                    if (isset($config['responses']['data']) && !$config['responses']['show_responses']){
+                        $responsesData = [];
+                    }else if (isset($config['responses']['data']) && $config['responses']['show_responses']===true){
+                        $responsesData = $config['responses']['data'];
+                    }else{
+                        $responsesData = $config['responses'];
+                    }
+                    foreach ($responsesData as $resItem){
+                        if (!empty($resItem['main']) && $resItem['main']===true){
+                            $resItem['params'] = $methodItem['return'];
+                            $hasMian=true;
+                        }
+                        $returned[]=$resItem;
+                    }
+                    if (!$hasMian){
+                        $returned = Utils::arrayMergeAndUnique("name",$returned, $methodItem['return']);
+//                        $returned = array_merge($returned,$methodItem['return']);
+                    }
+                    $methodItem['return']=$returned;
+                }
+                // 默认method
+                if (empty($methodItem['method'])){
+                    $methodItem['method']=!empty($config['default_method'])?$config['default_method']:'GET';
+                }
+                // 默认default_author
+                if (empty($methodItem['author']) && !empty($config['default_author']) && !in_array("NotDefaultAuthor",$textAnnotations)){
+                    $methodItem['author']=$config['default_author'];
+                }
+
+                // Tags
+                if (!empty($methodItem['tag'])){
+                    if (strpos($methodItem['tag'],' ')!==false){
+                        $tagArr = explode(" ", $methodItem['tag']);
+                        foreach ($tagArr as $tag){
+                            if (!in_array($tag,$this->tags)){
+                                $this->tags[]=$tag;
+                            }
+                        }
+                    }else if (!in_array($methodItem['tag'],$this->tags)){
+                        $this->tags[]=$methodItem['tag'];
+                    }
+                }
+
+                // 无url,自动生成
+                if(empty($methodItem['url'])){
+                    $methodItem['url'] = $this->autoCreateUrl($refMethod);
+                }else if (!empty($routeGroup->value)){
+                    // 路由分组,url加上分组
+                    $methodItem['url'] = '/'.$routeGroup->value.'/'.$methodItem['url'];
+                }
+                $methodItem['name']=$refMethod->name;
+                $methodItem['menu_key']=$methodItem['method']."_".$refMethod->name."_".mt_rand(10000,99999);
+
+                $methodList[]=$methodItem;
+
+            }
+
+        }
+        $data['children']=$methodList;
+        return $data;
+    }
+
+    /**
+     * 自动生成url
+     * @param $method
+     * @return string
+     */
+    protected function autoCreateUrl($method){
+        // 控制器地址
+        $pathArr = explode("\\", $method->class);
+        $filterPathNames = array("app", "controller");
+        $classPathArr=[];
+        foreach ($pathArr as $item){
+            if (!in_array($item,$filterPathNames)){
+                $classPathArr[]=$item;
+            }
+        }
+        $classPath = implode('/',$classPathArr);
+        return '/'.$classPath.'/'.$method->name;
+    }
+
+    /**
+     * ref引用
+     * @param $refPath
+     * @param bool $enableRefService
+     * @return false|string[]
+     */
+    protected function renderRef($refPath,$enableRefService=true){
+        $res = ['type'=>'model'];
+        // 通用定义引入
+        if(strpos($refPath,'\\')===false){
+            $config = $this->app->config->get('apidoc');
+            $refPath = $config['definitions'].'\\'.$refPath;
+            $data = $this->renderService($refPath);
+            $res['type']="service";
+            $res['data']=$data;
+            return $res;
+        }
+        // 模型引入
+        $modelData = (new ParseModel($this->reader))->renderModel($refPath);
+        if ($modelData !==false){
+            $res['data']=$modelData;
+            return $res;
+        }
+        if ($enableRefService===false){
+            return false;
+        }
+        $data = $this->renderService($refPath);
+        $res['type']="service";
+        $res['data']=$data;
+        return $res;
+    }
+
+    /**
+     * 解析注释引用
+     * @param $refPath
+     * @return array
+     * @throws \ReflectionException
+     */
+    protected function renderService($refPath){
+        $pathArr = explode("\\", $refPath);
+        $methodName = $pathArr[count($pathArr)-1];
+        unset($pathArr[count($pathArr)-1]);
+        $classPath = implode("\\", $pathArr);
+        $classReflect = new \ReflectionClass($classPath);
+        $methodName = trim ( $methodName );
+        $refMethod = $classReflect->getMethod($methodName);
+        $res = $this->parseAnnotation($refMethod,true);
+        return $res;
+    }
+
+    /**
+     * 处理Param/Returned的字段名name、params子级参数
+     * @param $values
+     * @return array
+     */
+    protected function handleParamValue($values,$field='param'){
+        $name="";
+        $params=[];
+        if (!empty($values) && is_array($values) && count($values)>0){
+            foreach ($values as $item){
+                if (is_string($item)){
+                    $name=$item;
+                }else if(is_object($item)){
+                    if (!empty($item->ref)){
+                        $refRes = $this->renderRef($item->ref,true);
+                        $params = $this->handleRefData($params,$refRes,$item,$field);
+                    }else{
+                        $param=[
+                            "name"=>"",
+                            "type"=>$item->type,
+                            "desc"=>$item->desc,
+                            "default"=>$item->default,
+                            "require"=>$item->require,
+                        ];
+                        $children = $this->handleParamValue($item->value);
+                        $param['name'] = $children['name'];
+                        if (count($children['params'])>0){
+                            $param['params'] = $children['params'];
+                        }
+                        $params[]=$param;
+                    }
+                }
+            }
+        }else{
+            $name=$values;
+        }
+        return ['name'=>$name,'params'=>$params];
+    }
+
+    /**
+     * 解析方法注释
+     * @param $refMethod
+     * @param bool $enableRefService 是否终止service的引入
+     * @return array
+     */
+    protected function parseAnnotation($refMethod,$enableRefService = true){
+        $data=[];
+        if ($annotations = $this->reader->getMethodAnnotations($refMethod)) {
+            $headers=[];
+            $params = [];
+            $returns = [];
+
+            foreach ($annotations as $annotation) {
+                switch (true) {
+                    case $annotation instanceof Param:
+                        if (!empty($annotation->ref)){
+                            $refRes = $this->renderRef($annotation->ref,$enableRefService);
+                            $params = $this->handleRefData($params,$refRes,$annotation,'param');
+                        }else{
+                            $param=[
+                                "name"=>"",
+                                "type"=>$annotation->type,
+                                "desc"=>$annotation->desc,
+                                "default"=>$annotation->default,
+                                "require"=>$annotation->require,
+                            ];
+                            $children=$this->handleParamValue($annotation->value,'param');
+                            $param['name']=$children['name'];
+                            if (count($children['params'])>0){
+                                $param['params']=$children['params'];
+                            }
+                            $params[] = $param;
+                            
+                        }
+                        break;
+                    case $annotation instanceof Returned:
+                        if (!empty($annotation->ref)){
+                            $refRes = $this->renderRef($annotation->ref,$enableRefService);
+                            $returns = $this->handleRefData($returns,$refRes,$annotation,'return');
+                        }else{
+                            $param=[
+                                "name"=>"",
+                                "type"=>$annotation->type,
+                                "desc"=>$annotation->desc,
+                                "default"=>$annotation->default,
+                                "require"=>$annotation->require,
+                            ];
+                            $children=$this->handleParamValue($annotation->value,'return');
+                            $param['name']=$children['name'];
+                            if (count($children['params'])>0){
+                                $param['params']=$children['params'];
+                            }
+                            $returns[] = $param;
+                        }
+                        break;
+                    case $annotation instanceof Header:
+                        if (!empty($annotation->ref)){
+                            $refRes = $this->renderRef($annotation->ref,$enableRefService);
+                            $headers = $this->handleRefData($headers,$refRes,$annotation,'header');
+                        }else {
+                            $param = [
+                                "name" => $annotation->value,
+                                "desc" => $annotation->desc,
+                                "require" => $annotation->require,
+                                "type"=>$annotation->type,
+                                "default"=>$annotation->default,
+                            ];
+                            $headers[] = $param;
+                        }
+                        break;
+                    case $annotation instanceof Route:
+                        if (empty($data['method'])){
+                            $data['method'] = $annotation->method;
+                        }
+                        if (empty($data['url'])){
+                            $data['url']=$annotation->value;
+                        }
+                        break;
+                    case $annotation instanceof Author:
+                        $data['author']=$annotation->value;
+                        break;
+
+                    case $annotation instanceof Title:
+                        $data['title']=$annotation->value;
+                        break;
+                    case $annotation instanceof Desc:
+                        $data['desc']=$annotation->value;
+                        break;
+                    case $annotation instanceof ParamType:
+                        $data['paramType']=$annotation->value;
+                        break;
+                    case $annotation instanceof Url:
+                        $data['url']=$annotation->value;
+                        break;
+                    case $annotation instanceof Method:
+                        $data['method']=$annotation->value;
+                        break;
+                    case $annotation instanceof Tag:
+                        $data['tag']=$annotation->value;
+                        break;
+                }
+            }
+            if ($headers && count($headers)>0){
+                $data['header']=$headers;
+            }
+            $data['param']=$params;
+            $data['return']=$returns;
+        }
+        return $data;
+    }
+
+    /**
+     * 解析非注解文本注释
+     * @param $refMethod
+     * @return array|false
+     */
+    protected function parseTextAnnotation($refMethod){
+        $annotation=$refMethod->getDocComment();
+        if (empty($annotation)){
+            return false;
+        }
+        if (preg_match ( '#^/\*\*(.*)\*/#s', $annotation, $comment ) === false)
+            return false;
+        $comment = trim ( $comment [1] );
+        if (preg_match_all ( '#^\s*\*(.*)#m', $comment, $lines ) === false)
+            return false;
+        $data=[];
+        foreach ( $lines[1] as $line ) {
+            $line = trim($line);
+            if (!empty ($line) && strpos ( $line, '@' ) !== 0) {
+                $data[]=$line;
+            }
+        }
+        return $data;
+    }
+
+
+    /**
+     * 处理param、returned 参数
+     * @param $params
+     * @param $refRes
+     * @param $annotation
+     * @return array
+     */
+    protected function handleRefData($params,$refRes,$annotation,$field){
+        if ($refRes['type']==="model" && count($refRes['data'])>0){
+            // 模型引入
+            $data = $refRes['data'];
+        }else if($refRes['type']==="service" && !empty($refRes['data']) && !empty($refRes['data'][$field]) ){
+            // service引入
+            $data=$refRes['data'][$field];
+        }else{
+            return $params;
+        }
+        // 过滤field
+        if (!empty($annotation->field)){
+            $data=(new Utils())->filterParamsField($data,$annotation->field,'field');
+        }
+        // 过滤withoutField
+        if (!empty($annotation->withoutField)){
+            $data=(new Utils())->filterParamsField($data,$annotation->withoutField,'withoutField');
+        }
+        
+        if (!empty($annotation->value)){
+            $item = [
+                'name'=>$annotation->value,
+                'desc'=>$annotation->desc,
+                'type'=>$annotation->type,
+                'require'=>$annotation->require,
+                'default'=>$annotation->default,
+                'params'=>$data
+            ];
+            if ($annotation->type === 'tree'){
+                // 类型为tree的
+                $item['params'][]= [
+                    'params'=>$data,
+                    'name'=>$annotation->childrenField,
+                    'type'=>'array',
+                    'desc'=>$annotation->childrenDesc,
+                ];
+                $params[]=$item;
+            }else{
+                $params[] = $item;
+            }
+        }else{
+            $params = array_merge($params,$data);
+        }
+        return $params;
+    }
+}

+ 53 - 0
vendor/hg/apidoc/src/ParseMarkdown.php

@@ -0,0 +1,53 @@
+<?php
+
+
+namespace hg\apidoc;
+
+
+trait ParseMarkdown
+{
+
+    protected function renderDocs($docs){
+        $docData = [];
+        if (!empty($docs) && count($docs)>0){
+            foreach ($docs as $item){
+
+                if (!empty($item['items']) && count($item['items'])>0){
+                    // 有子级
+                    $docList = [];
+                    foreach ($item['items'] as $doc){
+                        if (!empty($doc['path'])){
+                            $doc['content'] = $this->renderContent($doc['path']);
+                            $doc['type']='md';
+                            if (!empty($doc['content'])){
+                                $doc['menu_key']="md_".mt_rand(10000,99999);
+                                $docList[]=$doc;
+                            }
+                        }
+                    }
+                    $docData[]=[
+                        'title'=>$item['title'],
+                        'children'=>$docList,
+                        'group'=>'markdown_doc',
+                        'menu_key'=>"md_group_".mt_rand(10000,99999)
+                    ];
+                }else if(!empty($item['path'])){
+                    $item['content'] =  $this->renderContent($item['path']);
+                    $item['type']='md';
+                    if (!empty($item['content'])){
+                        $item['menu_key']="md_".mt_rand(10000,99999);
+                        $docData[]=$item;
+                    }
+                }
+            }
+        }
+        return $docData;
+    }
+
+    protected function renderContent($path){
+        $mdPath = (new Utils())->replaceCurrentAppTemplate($path,$this->currentApps);
+        $filePath = $this->app->getRootPath().$mdPath.'.md';
+        $contents = Utils::getFileContent($filePath);
+        return $contents;
+    }
+}

+ 160 - 0
vendor/hg/apidoc/src/ParseModel.php

@@ -0,0 +1,160 @@
+<?php
+namespace hg\apidoc;
+
+use Doctrine\Common\Annotations\Reader;
+use think\facade\Db;
+use hg\apidoc\annotation\Field;
+use hg\apidoc\annotation\WithoutField;
+use hg\apidoc\annotation\AddField;
+use think\helper\Str;
+
+class ParseModel
+{
+    protected $reader;
+
+    public function __construct(Reader $reader)
+    {
+        $this->reader = $reader;
+    }
+
+
+    public function renderModel($path){
+        $modelClassArr = explode("\\", $path);
+        $modelActionName = $modelClassArr[count($modelClassArr)-1];
+        $modelClassName = $modelClassArr[count($modelClassArr)-2];
+        unset($modelClassArr[count($modelClassArr)-1]);
+        $modelClassPath = implode("\\", $modelClassArr);
+        $classReflect = new \ReflectionClass($modelClassPath);
+        $modelActionName = trim ( $modelActionName );
+        $methodAction = $classReflect->getMethod($modelActionName);
+        // 获取所有模型属性
+        $propertys = $classReflect->getDefaultProperties();
+
+        // 获取表字段
+        $model = $this->getModel($methodAction,$modelClassName);
+        if (!is_callable(array($model,'getTable'))){
+            return false;
+        }
+        $table = $this->getTableDocument($model,$propertys);
+
+        // 模型注释-field
+        if ($fieldAnnotations = $this->reader->getMethodAnnotation($methodAction,Field::class)) {
+            $table = (new Utils())->filterParamsField($table,$fieldAnnotations->value,'field');
+        }
+        // 模型注释-withoutField
+        if ($fieldAnnotations = $this->reader->getMethodAnnotation($methodAction,WithoutField::class)) {
+            $table = (new Utils())->filterParamsField($table,$fieldAnnotations->value,'withoutField');
+        }
+        // 模型注释-addField
+        if ($annotations = $this->reader->getMethodAnnotations($methodAction)) {
+            foreach ($annotations as $annotation) {
+                switch (true) {
+                    case $annotation instanceof AddField:
+                        $param=[
+                            "name"=>$annotation->value,
+                            "desc"=>$annotation->desc,
+                            "require"=>$annotation->require,
+                            "type"=>$annotation->type,
+                            "default"=>$annotation->default
+                        ];
+                        $table[]=$param;
+                    break;
+                }
+            }
+        }
+        return $table;
+    }
+
+    public function getModel($method,$modelClassName){
+        if (!empty($method->class)){
+            $relationModelClass = $this->getIncludeClassName($method->class, $modelClassName);
+            if ($relationModelClass) {
+                $modelInstance = new $relationModelClass();
+                return $modelInstance;
+            } else {
+                return null;
+            }
+        }else{
+            return null;
+        }
+    }
+
+    protected function getClassFileContent($className)
+    {
+        if (class_exists($className)) {
+            $classReflect = new \ReflectionClass($className);
+        } else {
+            throw new \Exception("类不存在", '1');
+        }
+        if (!isset($this->classFileMaps[$className])) {
+            $this->classFileMaps[$className] = file_get_contents($classReflect->getFileName());
+        }
+        return $this->classFileMaps[$className];
+    }
+
+    protected function getIncludeClassName($mainClass, $class)
+    {
+
+            $classReflect = new \ReflectionClass($mainClass);
+            $possibleClass = $classReflect->getNamespaceName() . "\\" . $class;
+            if (class_exists($possibleClass)) {
+                return $possibleClass;
+            } else {
+                return "";
+            }
+    }
+
+    public function getTableDocument($model,$propertys)
+    {
+
+        $createSQL = Db::query("show create table " . $model->getTable())[0]['Create Table'];
+        preg_match_all("#`(.*?)`(.*?),#", $createSQL, $matches);
+        $fields = $matches[1];
+        $types = $matches[2];
+        $fieldComment = [];
+        //组织注释
+        for ($i = 0; $i < count($matches[0]); $i++) {
+            $key = $fields[$i];
+
+            $typeString = $types[$i];
+            $typeString = trim ( $typeString );
+            $typeArr = explode(' ' , $typeString);
+            $type = $typeArr[0];
+            $default="";
+            $require="0";
+            $desc ="";
+            if (strpos($typeString,'COMMENT') !== false){
+                // 存在字段注释
+                preg_match_all("#COMMENT\s*'(.*?)'#", $typeString, $edscs);
+                if (!empty($edscs[1]) && !empty($edscs[1][0]))
+                    $desc=$edscs[1][0];
+            }
+            if (strpos($typeString,'DEFAULT') !== false){
+                // 存在字段默认值
+                preg_match_all("#DEFAULT\s*'(.*?)'#", $typeString, $defaults);
+                if (!empty($defaults[1]) && !empty($defaults[1][0]))
+                    $default=$defaults[1][0];
+            }
+
+            if (strpos($typeString,'NOT NULL') !== false){
+                // 必填字段
+                $require="1";
+            }
+
+            $name = $key;
+            // 转换字段名为驼峰命名(用于输出)
+            if (isset($propertys['convertNameToCamel']) && $propertys['convertNameToCamel'] === true) {
+                $name = Str::camel($key);
+            }
+            $fieldComment[] = [
+                "name"=>$name,
+                "type"=>$type,
+                "desc"=>$desc,
+                "default"=>$default,
+                "require"=>$require,
+            ];
+        }
+        return $fieldComment;
+    }
+
+}

+ 40 - 0
vendor/hg/apidoc/src/Service.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace hg\apidoc;
+
+use Doctrine\Common\Annotations\AnnotationReader;
+use Doctrine\Common\Annotations\AnnotationRegistry;
+use Doctrine\Common\Annotations\Reader;
+use think\facade\Route;
+
+class Service extends \think\Service
+{
+
+    public function boot()
+    {
+
+        $this->registerRoutes(function (){
+
+            AnnotationReader::addGlobalIgnoredName('mixin');
+
+            // TODO: this method is deprecated and will be removed in doctrine/annotations 2.0
+            AnnotationRegistry::registerLoader('class_exists');
+
+            $this->app->bind(Reader::class, function () {
+                return new AnnotationReader();
+            });
+
+            $route_prefix = 'apidoc';
+            Route::group($route_prefix, function () {
+                $controller_namespace = '\hg\apidoc\Controller@';
+                Route::get('config'     , $controller_namespace . 'getConfig');
+                Route::get('data' , $controller_namespace . 'getData');
+                Route::post('auth'  , $controller_namespace . 'verifyAuth');
+                Route::post('crud'  , $controller_namespace . 'createCrud');
+            });
+        });
+
+    }
+
+
+}

+ 210 - 0
vendor/hg/apidoc/src/Utils.php

@@ -0,0 +1,210 @@
+<?php
+
+
+namespace hg\apidoc;
+
+
+class Utils
+{
+    /**
+     * 过滤参数字段
+     * @param $data
+     * @param $fields
+     * @param string $type
+     * @return array
+     */
+    public function filterParamsField($data,$fields,$type="field"){
+        $fieldArr=explode(',' , $fields);
+        $dataList=[];
+        foreach ($data as $item){
+            if (!empty($item['name']) && in_array($item['name'], $fieldArr) && $type==='field'){
+                $dataList[]=$item;
+            }else if (!(!empty($item['name']) && in_array($item['name'], $fieldArr)) && $type =="withoutField"){
+                $dataList[]=$item;
+            }
+        }
+        return $dataList;
+    }
+
+    /**
+     * 读取文件内容
+     * @param $fileName
+     * @return false|string
+     */
+    public static function getFileContent($fileName)
+    {
+        $content="";
+        if (file_exists($fileName)){
+            $handle = fopen($fileName, "r");
+            $content = fread($handle, filesize ($fileName));
+            fclose($handle);
+        }
+        return $content;
+    }
+
+    /**
+     * 保存文件
+     * @param $path
+     * @param $str_tmp
+     * @return bool
+     */
+    public static function createFile($path,$str_tmp){
+        $pathArr = explode("/", $path);
+        unset($pathArr[count($pathArr)-1]);
+        $dir = implode("/", $pathArr);
+        if (!file_exists($dir)) {
+            mkdir($dir, 0777, true);
+        }
+        $fp=fopen($path,"w") or die("Unable to open file!");
+        fwrite($fp,$str_tmp); //存入内容
+        fclose($fp);
+        return true;
+    }
+
+    /**
+     * 删除文件
+     * @param $path
+     */
+    public static function delFile($path){
+        $url=iconv('utf-8','gbk',$path);
+        if(PATH_SEPARATOR == ':'){ //linux
+            unlink($path);
+        }else{  //Windows
+            unlink($url);
+        }
+    }
+
+    /**
+     * 将tree树形数据转成list数据
+     * @param  array  $tree        tree数据
+     * @param  string $childName  子节点名称
+     * @return array  转换后的list数据
+     */
+    public function treeToList($tree,  $childName = 'children',$key="id",$parentField="parent")
+    {
+        $array = array();
+        foreach ($tree as $val) {
+            $array[] = $val;
+            if (isset($val[$childName])) {
+                $children = $this->treeToList($val[$childName], $childName);
+                if ($children) {
+                    $newChildren = [];
+                    foreach ($children as $item){
+                        $item[$parentField] = $val[$key];
+                        $newChildren[]=$item;
+                    }
+                    $array = array_merge($array, $newChildren);
+                }
+            }
+        }
+        return $array;
+    }
+
+    /**
+     * 根据一组keys获取所有关联节点
+     * @param $tree
+     * @param $keys
+     */
+    public function getTreeNodesByKeys($tree,$keys,$field="id",$childrenField="children"){
+        $list = $this->TreeToList($tree,$childrenField,"folder");
+        $data = [];
+        foreach ($keys as $k=>$v){
+            $parent = !$k?"":$keys[$k-1];
+            foreach ($list as $item){
+                if (((!empty($item['parent']) && $item['parent'] === $parent) || empty($item['parent'])) && $item[$field] == $v){
+                    $data[]=$item;
+                    break;
+                }
+            }
+        }
+        return $data;
+
+    }
+
+    /**
+     * 替换模板变量
+     * @param $temp
+     * @param $data
+     * @return string|string[]
+     */
+    public static function replaceTemplate($temp,$data){
+        $str = $temp;
+        foreach ($data as $k=>$v){
+            $key='${'.$k.'}';
+            if (strpos($str,$key)!==false){
+                $str=str_replace($key,$v,$str);
+            }
+        }
+        return $str;
+    }
+
+    /**
+     * 替换当前所选应用/版本的变量
+     * @param $temp
+     * @param $currentApps
+     * @return string|string[]
+     */
+    public function replaceCurrentAppTemplate($temp,$currentApps){
+        $str = $temp;
+        if (!empty($currentApps) && count($currentApps)>0){
+            $data = [];
+            for ($i=0; $i<=3; $i++) {
+                if (isset($currentApps[$i])){
+                    $appItem =$currentApps[$i];
+                    foreach ($appItem as $k=>$v){
+                        $key = 'app['.$i.'].'.$k;
+                        $data[$key]=$v;
+                    }
+                }else{
+                    $appItem=$currentApps[0];
+                    foreach ($appItem as $k=>$v){
+                        $key = 'app['.$i.'].'.$k;
+                        $data[$key]="";
+                    }
+                }
+            }
+            $str = $this->replaceTemplate($str,$data);
+        }
+        return $str;
+    }
+
+    public static function getArrayFind($array,$query){
+        $res=null;
+        if (is_array($array)){
+            foreach ($array as $item){
+                if ($query($item)){
+                    $res= $item;
+                    break;
+                }
+            }
+        }
+        return $res;
+    }
+
+    /**
+     * 合并对象数组并根据key去重
+     * @param string $name
+     * @param mixed ...$array
+     * @return array
+     */
+    public static function arrayMergeAndUnique($key="name",...$array){
+        $mergeArr=[];
+        foreach ($array as $k => $v) {
+            $mergeArr=array_merge($mergeArr,$v);
+        }
+        $keys = [];
+        foreach ($mergeArr as $k => $v) {
+            $keys[]=$v[$key];
+        }
+        $uniqueKeys=array_flip(array_flip($keys));
+        $newArray=[];
+        foreach ($uniqueKeys as $k=>$v){
+            $newArray[]=$mergeArr[$k];
+        }
+        return $newArray;
+
+    }
+
+
+
+}

+ 45 - 0
vendor/hg/apidoc/src/annotation/AddField.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+/**
+ * 添加模型的字段
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD"})
+ */
+class AddField extends Annotation
+{
+    /**
+     * 字段名
+     * @var string
+     */
+    public $name;
+    /**
+     * 类型
+     * @var string
+     */
+    public $type = 'string';
+
+
+    /**
+     * 默认值
+     * @var string
+     */
+    public $default;
+
+    /**
+     * 描述
+     * @var string
+     */
+    public $desc;
+
+    /**
+     * 必须
+     * @var bool
+     */
+    public $require = false;
+}

+ 15 - 0
vendor/hg/apidoc/src/annotation/Author.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+/**
+ * 作者
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD"})
+ */
+class Author extends Annotation
+{}

+ 15 - 0
vendor/hg/apidoc/src/annotation/Desc.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+/**
+ * 描述
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD","CLASS"})
+ */
+class Desc extends Annotation
+{}

+ 15 - 0
vendor/hg/apidoc/src/annotation/Field.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+/**
+ * 指定获取模型的字段
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD"})
+ */
+class Field extends Annotation
+{}

+ 15 - 0
vendor/hg/apidoc/src/annotation/Group.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+/**
+ * 分组
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"CLASS"})
+ */
+class Group extends Annotation
+{}

+ 41 - 0
vendor/hg/apidoc/src/annotation/Header.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+
+/**
+ * 请求头
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD"})
+ */
+class Header extends ParamBase
+{
+    /**
+     * 必须
+     * @var bool
+     */
+    public $require = false;
+
+    /**
+     * 类型
+     * @var string
+     */
+    public $type;
+
+    /**
+     * 引入
+     * @var string
+     */
+    public $ref;
+
+
+    /**
+     * 描述
+     * @var string
+     */
+    public $desc;
+
+
+}

+ 15 - 0
vendor/hg/apidoc/src/annotation/Method.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+/**
+ * Url
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD"})
+ */
+class Method extends Annotation
+{}

+ 28 - 0
vendor/hg/apidoc/src/annotation/Param.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+
+/**
+ * 参数
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD","ANNOTATION"})
+ */
+final class Param extends ParamBase
+{
+
+
+    /**
+     * 必须
+     * @var bool
+     */
+    public $require = false;
+    
+    /**
+     * 引入
+     * @var string
+     */
+    public $ref;
+}

+ 56 - 0
vendor/hg/apidoc/src/annotation/ParamBase.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+abstract class ParamBase extends Annotation
+{
+
+    /**
+     * 类型
+     * @Enum({"string", "integer", "int", "boolean", "array", "double", "object", "tree", "file","float","date","time","datetime"})
+     * @var string
+     */
+    public $type = 'string';
+
+
+    /**
+     * 默认值
+     * @var string
+     */
+    public $default;
+
+    /**
+     * 描述
+     * @var string
+     */
+    public $desc;
+
+    /**
+     * 为tree类型时指定children字段
+     * @var string
+     */
+    public $childrenField = 'children';
+
+    /**
+     * 为tree类型时指定children字段说明
+     * @var string
+     */
+    public $childrenDesc = 'children';
+
+    /**
+     * 指定引入的字段
+     * @var string
+     */
+    public $field;
+
+    /**
+     * 指定从引入中过滤的字段
+     * @var string
+     */
+    public $withoutField;
+
+
+
+}

+ 15 - 0
vendor/hg/apidoc/src/annotation/ParamType.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+/**
+ * 参数类型
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD"})
+ */
+class ParamType extends Annotation
+{}

+ 31 - 0
vendor/hg/apidoc/src/annotation/Returned.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+/**
+ * 说明
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD","ANNOTATION"})
+ */
+final class Returned extends ParamBase
+{
+
+    /**
+     * 必须
+     * @var bool
+     */
+    public $require = false;
+
+    /**
+     * 引入
+     * @var string
+     */
+    public $ref;
+
+
+
+}

+ 19 - 0
vendor/hg/apidoc/src/annotation/Route.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+use Doctrine\Common\Annotations\Annotation\Enum;
+use Doctrine\Common\Annotations\Annotation\Target;
+
+
+final class Route extends Rule
+{
+    /**
+     * 请求类型
+     * @Enum({"GET","POST","PUT","DELETE","PATCH","OPTIONS","HEAD"})
+     * @var string
+     */
+    public $method = "GET";
+
+}

+ 77 - 0
vendor/hg/apidoc/src/annotation/Rule.php

@@ -0,0 +1,77 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+abstract class Rule extends Annotation
+{
+    /**
+     * @var string|array
+     */
+    public $middleware;
+
+    /**
+     * 后缀
+     * @var string
+     */
+    public $ext;
+
+    /**
+     * @var string
+     */
+    public $deny_ext;
+
+    /**
+     * @var bool
+     */
+    public $https;
+
+    /**
+     * @var string
+     */
+    public $domain;
+
+    /**
+     * @var bool
+     */
+    public $complete_match;
+
+    /**
+     * @var string|array
+     */
+    public $cache;
+
+    /**
+     * @var bool
+     */
+    public $ajax;
+
+    /**
+     * @var bool
+     */
+    public $pjax;
+
+    /**
+     * @var bool
+     */
+    public $json;
+
+    /**
+     * @var array
+     */
+    public $filter;
+
+    /**
+     * @var array
+     */
+    public $append;
+
+    public function getOptions()
+    {
+        return array_intersect_key(get_object_vars($this), array_flip([
+            'middleware', 'ext', 'deny_ext', 'https', 'domain', 'complete_match', 'cache', 'ajax', 'pjax', 'json', 'filter', 'append',
+        ]));
+    }
+
+}

+ 15 - 0
vendor/hg/apidoc/src/annotation/Tag.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+/**
+ * Tag
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD"})
+ */
+class Tag extends Annotation
+{}

+ 15 - 0
vendor/hg/apidoc/src/annotation/Title.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+/**
+ * 标题
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD","CLASS"})
+ */
+class Title extends Annotation
+{}

+ 15 - 0
vendor/hg/apidoc/src/annotation/Url.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+/**
+ * Url
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD"})
+ */
+class Url extends Annotation
+{}

+ 15 - 0
vendor/hg/apidoc/src/annotation/WithoutField.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace hg\apidoc\annotation;
+
+use Doctrine\Common\Annotations\Annotation;
+
+/**
+ * 排除模型的字段
+ *
+ * @package hg\apidoc\annotation
+ * @Annotation
+ * @Target({"METHOD"})
+ */
+class WithoutField extends Annotation
+{}

+ 62 - 0
vendor/hg/apidoc/src/config.php

@@ -0,0 +1,62 @@
+<?php
+return [
+    // 文档标题
+    'title'              => 'APi接口文档',
+    // 文档描述
+    'desc'               => '',
+    // 版权申明
+    'copyright'          => 'Powered By hg-code',
+    // 默认作者
+    'default_author'=>'',
+    // 默认请求类型
+    'default_method'=>'GET',
+    // 设置应用/版本(必须设置)
+    'apps'           => [
+        ['title'=>'v1.0','path'=>'app\controller','folder'=>'v1'],
+    ],
+    // 控制器分组
+    'groups'             => [],
+    // 指定公共注释定义的文件地址
+    'definitions'        => "app\controller\Definitions",
+    //指定生成文档的控制器
+    'controllers'        => [],
+    // 过滤,不解析的控制器
+    'filter_controllers' => [],
+    // 缓存配置
+    'cache'              => [
+        // 是否开启缓存
+        'enable' => false,
+        // 缓存文件路径
+        'path'   =>  '../runtime/apidoc/',
+        // 是否显示更新缓存按钮
+        'reload' => true,
+        // 最大缓存文件数
+        'max'    => 5,  //最大缓存数量
+    ],
+    // 权限认证配置
+    'auth'               => [
+        // 是否启用密码验证
+        'enable'     => false,
+        // 验证密码
+        'password'   => "123456",
+        // 密码加密盐
+        'secret_key' => "apidoc#hg_code",
+    ],
+    // 统一的请求Header
+    'headers'=>[],
+    // 统一的请求参数Parameters
+    'parameters'=>[],
+    // 统一的请求响应体,仅显示在文档提示中
+    'responses'=>[
+        ['name'=>'code','desc'=>'状态码','type'=>'int'],
+        ['name'=>'message','desc'=>'操作描述','type'=>'string'],
+        ['name'=>'data','desc'=>'业务数据','main'=>true,'type'=>'object'],
+    ],
+    // md文档
+    'docs'              => [
+        'menu_title' => '开发文档',
+        'menus'      => []
+    ],
+    'crud'=>[]
+
+];

+ 3 - 2
vendor/services.php

@@ -1,6 +1,7 @@
 <?php 
-// This file is automatically generated at:2021-03-19 09:54:37
+// This file is automatically generated at:2021-03-19 09:58:55
 declare (strict_types = 1);
 return array (
-  0 => 'think\\admin\\Library',
+  0 => 'hg\\apidoc\\Service',
+  1 => 'think\\admin\\Library',
 );

+ 3 - 0
vendor/symfony/class-loader/.gitignore

@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml

+ 141 - 0
vendor/symfony/class-loader/ApcClassLoader.php

@@ -0,0 +1,141 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader;
+
+/**
+ * ApcClassLoader implements a wrapping autoloader cached in APC for PHP 5.3.
+ *
+ * It expects an object implementing a findFile method to find the file. This
+ * allows using it as a wrapper around the other loaders of the component (the
+ * ClassLoader for instance) but also around any other autoloaders following
+ * this convention (the Composer one for instance).
+ *
+ *     // with a Symfony autoloader
+ *     use Symfony\Component\ClassLoader\ClassLoader;
+ *
+ *     $loader = new ClassLoader();
+ *     $loader->addPrefix('Symfony\Component', __DIR__.'/component');
+ *     $loader->addPrefix('Symfony',           __DIR__.'/framework');
+ *
+ *     // or with a Composer autoloader
+ *     use Composer\Autoload\ClassLoader;
+ *
+ *     $loader = new ClassLoader();
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     $cachedLoader = new ApcClassLoader('my_prefix', $loader);
+ *
+ *     // activate the cached autoloader
+ *     $cachedLoader->register();
+ *
+ *     // eventually deactivate the non-cached loader if it was registered previously
+ *     // to be sure to use the cached one.
+ *     $loader->unregister();
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Kris Wallsmith <kris@symfony.com>
+ */
+class ApcClassLoader
+{
+    private $prefix;
+
+    /**
+     * A class loader object that implements the findFile() method.
+     *
+     * @var object
+     */
+    protected $decorated;
+
+    /**
+     * Constructor.
+     *
+     * @param string $prefix    The APC namespace prefix to use
+     * @param object $decorated A class loader object that implements the findFile() method
+     *
+     * @throws \RuntimeException
+     * @throws \InvalidArgumentException
+     */
+    public function __construct($prefix, $decorated)
+    {
+        if (!function_exists('apcu_fetch')) {
+            throw new \RuntimeException('Unable to use ApcClassLoader as APC is not installed.');
+        }
+
+        if (!method_exists($decorated, 'findFile')) {
+            throw new \InvalidArgumentException('The class finder must implement a "findFile" method.');
+        }
+
+        $this->prefix = $prefix;
+        $this->decorated = $decorated;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param string $class The name of the class
+     *
+     * @return bool|null True, if loaded
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            require $file;
+
+            return true;
+        }
+    }
+
+    /**
+     * Finds a file by class name while caching lookups to APC.
+     *
+     * @param string $class A class name to resolve to file
+     *
+     * @return string|null
+     */
+    public function findFile($class)
+    {
+        $file = apcu_fetch($this->prefix.$class, $success);
+
+        if (!$success) {
+            apcu_store($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null);
+        }
+
+        return $file;
+    }
+
+    /**
+     * Passes through all unknown calls onto the decorated object.
+     */
+    public function __call($method, $args)
+    {
+        return call_user_func_array(array($this->decorated, $method), $args);
+    }
+}

+ 36 - 0
vendor/symfony/class-loader/CHANGELOG.md

@@ -0,0 +1,36 @@
+CHANGELOG
+=========
+
+3.0.0
+-----
+
+ * The DebugClassLoader class has been removed
+ * The DebugUniversalClassLoader class has been removed
+ * The UniversalClassLoader class has been removed
+ * The ApcUniversalClassLoader class has been removed
+
+2.4.0
+-----
+
+ * deprecated the UniversalClassLoader in favor of the ClassLoader class instead
+ * deprecated the ApcUniversalClassLoader in favor of the ApcClassLoader class instead
+ * deprecated the DebugUniversalClassLoader in favor of the DebugClassLoader class from the Debug component
+ * deprecated the DebugClassLoader as it has been moved to the Debug component instead
+
+2.3.0
+-----
+
+ * added a WinCacheClassLoader for WinCache
+
+2.1.0
+-----
+
+ * added a DebugClassLoader able to wrap any autoloader providing a findFile
+   method
+ * added a new ApcClassLoader and XcacheClassLoader using composition to wrap
+   other loaders
+ * added a new ClassLoader which does not distinguish between namespaced and
+   pear-like classes (as the PEAR convention is a subset of PSR-0) and
+   supports using Composer's namespace maps
+ * added a class map generator
+ * added support for loading globally-installed PEAR packages

+ 444 - 0
vendor/symfony/class-loader/ClassCollectionLoader.php

@@ -0,0 +1,444 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader;
+
+/**
+ * ClassCollectionLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ClassCollectionLoader
+{
+    private static $loaded;
+    private static $seen;
+    private static $useTokenizer = true;
+
+    /**
+     * Loads a list of classes and caches them in one big file.
+     *
+     * @param array  $classes    An array of classes to load
+     * @param string $cacheDir   A cache directory
+     * @param string $name       The cache name prefix
+     * @param bool   $autoReload Whether to flush the cache when the cache is stale or not
+     * @param bool   $adaptive   Whether to remove already declared classes or not
+     * @param string $extension  File extension of the resulting file
+     *
+     * @throws \InvalidArgumentException When class can't be loaded
+     */
+    public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php')
+    {
+        // each $name can only be loaded once per PHP process
+        if (isset(self::$loaded[$name])) {
+            return;
+        }
+
+        self::$loaded[$name] = true;
+
+        if ($adaptive) {
+            $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits());
+
+            // don't include already declared classes
+            $classes = array_diff($classes, $declared);
+
+            // the cache is different depending on which classes are already declared
+            $name = $name.'-'.substr(hash('sha256', implode('|', $classes)), 0, 5);
+        }
+
+        $classes = array_unique($classes);
+
+        // cache the core classes
+        if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) {
+            throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir));
+        }
+        $cacheDir = rtrim(realpath($cacheDir) ?: $cacheDir, '/'.DIRECTORY_SEPARATOR);
+        $cache = $cacheDir.'/'.$name.$extension;
+
+        // auto-reload
+        $reload = false;
+        if ($autoReload) {
+            $metadata = $cache.'.meta';
+            if (!is_file($metadata) || !is_file($cache)) {
+                $reload = true;
+            } else {
+                $time = filemtime($cache);
+                $meta = unserialize(file_get_contents($metadata));
+
+                sort($meta[1]);
+                sort($classes);
+
+                if ($meta[1] != $classes) {
+                    $reload = true;
+                } else {
+                    foreach ($meta[0] as $resource) {
+                        if (!is_file($resource) || filemtime($resource) > $time) {
+                            $reload = true;
+
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!$reload && file_exists($cache)) {
+            require_once $cache;
+
+            return;
+        }
+        if (!$adaptive) {
+            $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits());
+        }
+
+        $files = self::inline($classes, $cache, $declared);
+
+        if ($autoReload) {
+            // save the resources
+            self::writeCacheFile($metadata, serialize(array(array_values($files), $classes)));
+        }
+    }
+
+    /**
+     * Generates a file where classes and their parents are inlined.
+     *
+     * @param array  $classes  An array of classes to load
+     * @param string $cache    The file where classes are inlined
+     * @param array  $excluded An array of classes that won't be inlined
+     *
+     * @return array The source map of inlined classes, with classes as keys and files as values
+     *
+     * @throws \RuntimeException When class can't be loaded
+     */
+    public static function inline($classes, $cache, array $excluded)
+    {
+        $declared = array();
+        foreach (self::getOrderedClasses($excluded) as $class) {
+            $declared[$class->getName()] = true;
+        }
+
+        // cache the core classes
+        $cacheDir = dirname($cache);
+        if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) {
+            throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir));
+        }
+
+        $spacesRegex = '(?:\s*+(?:(?:\#|//)[^\n]*+\n|/\*(?:(?<!\*/).)++)?+)*+';
+        $dontInlineRegex = <<<REGEX
+            '(?:
+               ^<\?php\s.declare.\(.strict_types.=.1.\).;
+               | \b__halt_compiler.\(.\)
+               | \b__(?:DIR|FILE)__\b
+            )'isx
+REGEX;
+        $dontInlineRegex = str_replace('.', $spacesRegex, $dontInlineRegex);
+
+        $cacheDir = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $cacheDir));
+        $files = array();
+        $content = '';
+        foreach (self::getOrderedClasses($classes) as $class) {
+            if (isset($declared[$class->getName()])) {
+                continue;
+            }
+            $declared[$class->getName()] = true;
+
+            $files[$class->getName()] = $file = $class->getFileName();
+            $c = file_get_contents($file);
+
+            if (preg_match($dontInlineRegex, $c)) {
+                $file = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $file));
+
+                for ($i = 0; isset($file[$i], $cacheDir[$i]); ++$i) {
+                    if ($file[$i] !== $cacheDir[$i]) {
+                        break;
+                    }
+                }
+                if (1 >= $i) {
+                    $file = var_export(implode('/', $file), true);
+                } else {
+                    $file = array_slice($file, $i);
+                    $file = str_repeat('../', count($cacheDir) - $i).implode('/', $file);
+                    $file = '__DIR__.'.var_export('/'.$file, true);
+                }
+
+                $c = "\nnamespace {require $file;}";
+            } else {
+                $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', $c);
+
+                // fakes namespace declaration for global code
+                if (!$class->inNamespace()) {
+                    $c = "\nnamespace\n{\n".$c."\n}\n";
+                }
+
+                $c = self::fixNamespaceDeclarations('<?php '.$c);
+                $c = preg_replace('/^\s*<\?php/', '', $c);
+            }
+
+            $content .= $c;
+        }
+        self::writeCacheFile($cache, '<?php '.$content);
+
+        return $files;
+    }
+
+    /**
+     * Adds brackets around each namespace if it's not already the case.
+     *
+     * @param string $source Namespace string
+     *
+     * @return string Namespaces with brackets
+     */
+    public static function fixNamespaceDeclarations($source)
+    {
+        if (!function_exists('token_get_all') || !self::$useTokenizer) {
+            if (preg_match('/(^|\s)namespace(.*?)\s*;/', $source)) {
+                $source = preg_replace('/(^|\s)namespace(.*?)\s*;/', "$1namespace$2\n{", $source)."}\n";
+            }
+
+            return $source;
+        }
+
+        $rawChunk = '';
+        $output = '';
+        $inNamespace = false;
+        $tokens = token_get_all($source);
+
+        for ($i = 0; isset($tokens[$i]); ++$i) {
+            $token = $tokens[$i];
+            if (!isset($token[1]) || 'b"' === $token) {
+                $rawChunk .= $token;
+            } elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
+                // strip comments
+                continue;
+            } elseif (T_NAMESPACE === $token[0]) {
+                if ($inNamespace) {
+                    $rawChunk .= "}\n";
+                }
+                $rawChunk .= $token[1];
+
+                // namespace name and whitespaces
+                while (isset($tokens[++$i][1]) && in_array($tokens[$i][0], array(T_WHITESPACE, T_NS_SEPARATOR, T_STRING))) {
+                    $rawChunk .= $tokens[$i][1];
+                }
+                if ('{' === $tokens[$i]) {
+                    $inNamespace = false;
+                    --$i;
+                } else {
+                    $rawChunk = rtrim($rawChunk)."\n{";
+                    $inNamespace = true;
+                }
+            } elseif (T_START_HEREDOC === $token[0]) {
+                $output .= self::compressCode($rawChunk).$token[1];
+                do {
+                    $token = $tokens[++$i];
+                    $output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token;
+                } while ($token[0] !== T_END_HEREDOC);
+                $output .= "\n";
+                $rawChunk = '';
+            } elseif (T_CONSTANT_ENCAPSED_STRING === $token[0]) {
+                $output .= self::compressCode($rawChunk).$token[1];
+                $rawChunk = '';
+            } else {
+                $rawChunk .= $token[1];
+            }
+        }
+
+        if ($inNamespace) {
+            $rawChunk .= "}\n";
+        }
+
+        $output .= self::compressCode($rawChunk);
+
+        if (\PHP_VERSION_ID >= 70000) {
+            // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
+            unset($tokens, $rawChunk);
+            gc_mem_caches();
+        }
+
+        return $output;
+    }
+
+    /**
+     * This method is only useful for testing.
+     */
+    public static function enableTokenizer($bool)
+    {
+        self::$useTokenizer = (bool) $bool;
+    }
+
+    /**
+     * Strips leading & trailing ws, multiple EOL, multiple ws.
+     *
+     * @param string $code Original PHP code
+     *
+     * @return string compressed code
+     */
+    private static function compressCode($code)
+    {
+        return preg_replace(
+            array('/^\s+/m', '/\s+$/m', '/([\n\r]+ *[\n\r]+)+/', '/[ \t]+/'),
+            array('', '', "\n", ' '),
+            $code
+        );
+    }
+
+    /**
+     * Writes a cache file.
+     *
+     * @param string $file    Filename
+     * @param string $content Temporary file content
+     *
+     * @throws \RuntimeException when a cache file cannot be written
+     */
+    private static function writeCacheFile($file, $content)
+    {
+        $dir = dirname($file);
+        if (!is_writable($dir)) {
+            throw new \RuntimeException(sprintf('Cache directory "%s" is not writable.', $dir));
+        }
+
+        $tmpFile = tempnam($dir, basename($file));
+
+        if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
+            @chmod($file, 0666 & ~umask());
+
+            return;
+        }
+
+        throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
+    }
+
+    /**
+     * Gets an ordered array of passed classes including all their dependencies.
+     *
+     * @param array $classes
+     *
+     * @return \ReflectionClass[] An array of sorted \ReflectionClass instances (dependencies added if needed)
+     *
+     * @throws \InvalidArgumentException When a class can't be loaded
+     */
+    private static function getOrderedClasses(array $classes)
+    {
+        $map = array();
+        self::$seen = array();
+        foreach ($classes as $class) {
+            try {
+                $reflectionClass = new \ReflectionClass($class);
+            } catch (\ReflectionException $e) {
+                throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
+            }
+
+            $map = array_merge($map, self::getClassHierarchy($reflectionClass));
+        }
+
+        return $map;
+    }
+
+    private static function getClassHierarchy(\ReflectionClass $class)
+    {
+        if (isset(self::$seen[$class->getName()])) {
+            return array();
+        }
+
+        self::$seen[$class->getName()] = true;
+
+        $classes = array($class);
+        $parent = $class;
+        while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) {
+            self::$seen[$parent->getName()] = true;
+
+            array_unshift($classes, $parent);
+        }
+
+        $traits = array();
+
+        foreach ($classes as $c) {
+            foreach (self::resolveDependencies(self::computeTraitDeps($c), $c) as $trait) {
+                if ($trait !== $c) {
+                    $traits[] = $trait;
+                }
+            }
+        }
+
+        return array_merge(self::getInterfaces($class), $traits, $classes);
+    }
+
+    private static function getInterfaces(\ReflectionClass $class)
+    {
+        $classes = array();
+
+        foreach ($class->getInterfaces() as $interface) {
+            $classes = array_merge($classes, self::getInterfaces($interface));
+        }
+
+        if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) {
+            self::$seen[$class->getName()] = true;
+
+            $classes[] = $class;
+        }
+
+        return $classes;
+    }
+
+    private static function computeTraitDeps(\ReflectionClass $class)
+    {
+        $traits = $class->getTraits();
+        $deps = array($class->getName() => $traits);
+        while ($trait = array_pop($traits)) {
+            if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) {
+                self::$seen[$trait->getName()] = true;
+                $traitDeps = $trait->getTraits();
+                $deps[$trait->getName()] = $traitDeps;
+                $traits = array_merge($traits, $traitDeps);
+            }
+        }
+
+        return $deps;
+    }
+
+    /**
+     * Dependencies resolution.
+     *
+     * This function does not check for circular dependencies as it should never
+     * occur with PHP traits.
+     *
+     * @param array            $tree       The dependency tree
+     * @param \ReflectionClass $node       The node
+     * @param \ArrayObject     $resolved   An array of already resolved dependencies
+     * @param \ArrayObject     $unresolved An array of dependencies to be resolved
+     *
+     * @return \ArrayObject The dependencies for the given node
+     *
+     * @throws \RuntimeException if a circular dependency is detected
+     */
+    private static function resolveDependencies(array $tree, $node, \ArrayObject $resolved = null, \ArrayObject $unresolved = null)
+    {
+        if (null === $resolved) {
+            $resolved = new \ArrayObject();
+        }
+        if (null === $unresolved) {
+            $unresolved = new \ArrayObject();
+        }
+        $nodeName = $node->getName();
+
+        if (isset($tree[$nodeName])) {
+            $unresolved[$nodeName] = $node;
+            foreach ($tree[$nodeName] as $dependency) {
+                if (!$resolved->offsetExists($dependency->getName())) {
+                    self::resolveDependencies($tree, $dependency, $resolved, $unresolved);
+                }
+            }
+            $resolved[$nodeName] = $node;
+            unset($unresolved[$nodeName]);
+        }
+
+        return $resolved;
+    }
+}

+ 203 - 0
vendor/symfony/class-loader/ClassLoader.php

@@ -0,0 +1,203 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader;
+
+/**
+ * ClassLoader implements an PSR-0 class loader.
+ *
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
+ *
+ *     $loader = new ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->addPrefix('Symfony\Component', __DIR__.'/component');
+ *     $loader->addPrefix('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (e.g. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class ClassLoader
+{
+    private $prefixes = array();
+    private $fallbackDirs = array();
+    private $useIncludePath = false;
+
+    /**
+     * Returns prefixes.
+     *
+     * @return array
+     */
+    public function getPrefixes()
+    {
+        return $this->prefixes;
+    }
+
+    /**
+     * Returns fallback directories.
+     *
+     * @return array
+     */
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirs;
+    }
+
+    /**
+     * Adds prefixes.
+     *
+     * @param array $prefixes Prefixes to add
+     */
+    public function addPrefixes(array $prefixes)
+    {
+        foreach ($prefixes as $prefix => $path) {
+            $this->addPrefix($prefix, $path);
+        }
+    }
+
+    /**
+     * Registers a set of classes.
+     *
+     * @param string       $prefix The classes prefix
+     * @param array|string $paths  The location(s) of the classes
+     */
+    public function addPrefix($prefix, $paths)
+    {
+        if (!$prefix) {
+            foreach ((array) $paths as $path) {
+                $this->fallbackDirs[] = $path;
+            }
+
+            return;
+        }
+        if (isset($this->prefixes[$prefix])) {
+            if (is_array($paths)) {
+                $this->prefixes[$prefix] = array_unique(array_merge(
+                    $this->prefixes[$prefix],
+                    $paths
+                ));
+            } elseif (!in_array($paths, $this->prefixes[$prefix])) {
+                $this->prefixes[$prefix][] = $paths;
+            }
+        } else {
+            $this->prefixes[$prefix] = array_unique((array) $paths);
+        }
+    }
+
+    /**
+     * Turns on searching the include for class files.
+     *
+     * @param bool $useIncludePath
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = (bool) $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param string $class The name of the class
+     *
+     * @return bool|null True, if loaded
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            require $file;
+
+            return true;
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|null The path, if found
+     */
+    public function findFile($class)
+    {
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)).DIRECTORY_SEPARATOR;
+            $className = substr($class, $pos + 1);
+        } else {
+            // PEAR-like class name
+            $classPath = null;
+            $className = $class;
+        }
+
+        $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className).'.php';
+
+        foreach ($this->prefixes as $prefix => $dirs) {
+            if ($class === strstr($class, $prefix)) {
+                foreach ($dirs as $dir) {
+                    if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) {
+                        return $dir.DIRECTORY_SEPARATOR.$classPath;
+                    }
+                }
+            }
+        }
+
+        foreach ($this->fallbackDirs as $dir) {
+            if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) {
+                return $dir.DIRECTORY_SEPARATOR.$classPath;
+            }
+        }
+
+        if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) {
+            return $file;
+        }
+    }
+}

+ 156 - 0
vendor/symfony/class-loader/ClassMapGenerator.php

@@ -0,0 +1,156 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader;
+
+/**
+ * ClassMapGenerator.
+ *
+ * @author Gyula Sallai <salla016@gmail.com>
+ */
+class ClassMapGenerator
+{
+    /**
+     * Generate a class map file.
+     *
+     * @param array|string $dirs Directories or a single path to search in
+     * @param string       $file The name of the class map file
+     */
+    public static function dump($dirs, $file)
+    {
+        $dirs = (array) $dirs;
+        $maps = array();
+
+        foreach ($dirs as $dir) {
+            $maps = array_merge($maps, static::createMap($dir));
+        }
+
+        file_put_contents($file, sprintf('<?php return %s;', var_export($maps, true)));
+    }
+
+    /**
+     * Iterate over all files in the given directory searching for classes.
+     *
+     * @param \Iterator|string $dir The directory to search in or an iterator
+     *
+     * @return array A class map array
+     */
+    public static function createMap($dir)
+    {
+        if (is_string($dir)) {
+            $dir = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir));
+        }
+
+        $map = array();
+
+        foreach ($dir as $file) {
+            if (!$file->isFile()) {
+                continue;
+            }
+
+            $path = $file->getRealPath() ?: $file->getPathname();
+
+            if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') {
+                continue;
+            }
+
+            $classes = self::findClasses($path);
+
+            if (\PHP_VERSION_ID >= 70000) {
+                // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
+                gc_mem_caches();
+            }
+
+            foreach ($classes as $class) {
+                $map[$class] = $path;
+            }
+        }
+
+        return $map;
+    }
+
+    /**
+     * Extract the classes in the given file.
+     *
+     * @param string $path The file to check
+     *
+     * @return array The found classes
+     */
+    private static function findClasses($path)
+    {
+        $contents = file_get_contents($path);
+        $tokens = token_get_all($contents);
+
+        $classes = array();
+
+        $namespace = '';
+        for ($i = 0; isset($tokens[$i]); ++$i) {
+            $token = $tokens[$i];
+
+            if (!isset($token[1])) {
+                continue;
+            }
+
+            $class = '';
+
+            switch ($token[0]) {
+                case T_NAMESPACE:
+                    $namespace = '';
+                    // If there is a namespace, extract it
+                    while (isset($tokens[++$i][1])) {
+                        if (in_array($tokens[$i][0], array(T_STRING, T_NS_SEPARATOR))) {
+                            $namespace .= $tokens[$i][1];
+                        }
+                    }
+                    $namespace .= '\\';
+                    break;
+                case T_CLASS:
+                case T_INTERFACE:
+                case T_TRAIT:
+                    // Skip usage of ::class constant
+                    $isClassConstant = false;
+                    for ($j = $i - 1; $j > 0; --$j) {
+                        if (!isset($tokens[$j][1])) {
+                            break;
+                        }
+
+                        if (T_DOUBLE_COLON === $tokens[$j][0]) {
+                            $isClassConstant = true;
+                            break;
+                        } elseif (!in_array($tokens[$j][0], array(T_WHITESPACE, T_DOC_COMMENT, T_COMMENT))) {
+                            break;
+                        }
+                    }
+
+                    if ($isClassConstant) {
+                        break;
+                    }
+
+                    // Find the classname
+                    while (isset($tokens[++$i][1])) {
+                        $t = $tokens[$i];
+                        if (T_STRING === $t[0]) {
+                            $class .= $t[1];
+                        } elseif ('' !== $class && T_WHITESPACE === $t[0]) {
+                            break;
+                        }
+                    }
+
+                    $classes[] = ltrim($namespace.$class, '\\');
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        return $classes;
+    }
+}

+ 19 - 0
vendor/symfony/class-loader/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2004-2017 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 68 - 0
vendor/symfony/class-loader/MapClassLoader.php

@@ -0,0 +1,68 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader;
+
+/**
+ * A class loader that uses a mapping file to look up paths.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class MapClassLoader
+{
+    private $map = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $map A map where keys are classes and values the absolute file path
+     */
+    public function __construct(array $map)
+    {
+        $this->map = $map;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param string $class The name of the class
+     */
+    public function loadClass($class)
+    {
+        if (isset($this->map[$class])) {
+            require $this->map[$class];
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|null The path, if found
+     */
+    public function findFile($class)
+    {
+        if (isset($this->map[$class])) {
+            return $this->map[$class];
+        }
+    }
+}

+ 93 - 0
vendor/symfony/class-loader/Psr4ClassLoader.php

@@ -0,0 +1,93 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader;
+
+/**
+ * A PSR-4 compatible class loader.
+ *
+ * See http://www.php-fig.org/psr/psr-4/
+ *
+ * @author Alexander M. Turek <me@derrabus.de>
+ */
+class Psr4ClassLoader
+{
+    /**
+     * @var array
+     */
+    private $prefixes = array();
+
+    /**
+     * @param string $prefix
+     * @param string $baseDir
+     */
+    public function addPrefix($prefix, $baseDir)
+    {
+        $prefix = trim($prefix, '\\').'\\';
+        $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
+        $this->prefixes[] = array($prefix, $baseDir);
+    }
+
+    /**
+     * @param string $class
+     *
+     * @return string|null
+     */
+    public function findFile($class)
+    {
+        $class = ltrim($class, '\\');
+
+        foreach ($this->prefixes as list($currentPrefix, $currentBaseDir)) {
+            if (0 === strpos($class, $currentPrefix)) {
+                $classWithoutPrefix = substr($class, strlen($currentPrefix));
+                $file = $currentBaseDir.str_replace('\\', DIRECTORY_SEPARATOR, $classWithoutPrefix).'.php';
+                if (file_exists($file)) {
+                    return $file;
+                }
+            }
+        }
+    }
+
+    /**
+     * @param string $class
+     *
+     * @return bool
+     */
+    public function loadClass($class)
+    {
+        $file = $this->findFile($class);
+        if (null !== $file) {
+            require $file;
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Removes this instance from the registered autoloaders.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+    }
+}

+ 14 - 0
vendor/symfony/class-loader/README.md

@@ -0,0 +1,14 @@
+ClassLoader Component
+=====================
+
+The ClassLoader component provides tools to autoload your classes and cache
+their locations for performance.
+
+Resources
+---------
+
+  * [Documentation](https://symfony.com/doc/current/components/class_loader/index.html)
+  * [Contributing](https://symfony.com/doc/current/contributing/index.html)
+  * [Report issues](https://github.com/symfony/symfony/issues) and
+    [send Pull Requests](https://github.com/symfony/symfony/pulls)
+    in the [main Symfony repository](https://github.com/symfony/symfony)

+ 197 - 0
vendor/symfony/class-loader/Tests/ApcClassLoaderTest.php

@@ -0,0 +1,197 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\ClassLoader\ApcClassLoader;
+use Symfony\Component\ClassLoader\ClassLoader;
+
+class ApcClassLoaderTest extends TestCase
+{
+    protected function setUp()
+    {
+        if (!(ini_get('apc.enabled') && ini_get('apc.enable_cli'))) {
+            $this->markTestSkipped('The apc extension is not enabled.');
+        } else {
+            apcu_clear_cache();
+        }
+    }
+
+    protected function tearDown()
+    {
+        if (ini_get('apc.enabled') && ini_get('apc.enable_cli')) {
+            apcu_clear_cache();
+        }
+    }
+
+    public function testConstructor()
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefix('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+
+        $loader = new ApcClassLoader('test.prefix.', $loader);
+
+        $this->assertEquals($loader->findFile('\Apc\Namespaced\FooBar'), apcu_fetch('test.prefix.\Apc\Namespaced\FooBar'), '__construct() takes a prefix as its first argument');
+    }
+
+    /**
+     * @dataProvider getLoadClassTests
+     */
+    public function testLoadClass($className, $testClassName, $message)
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefix('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->addPrefix('Apc_Pearlike_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+
+        $loader = new ApcClassLoader('test.prefix.', $loader);
+        $loader->loadClass($testClassName);
+        $this->assertTrue(class_exists($className), $message);
+    }
+
+    public function getLoadClassTests()
+    {
+        return array(
+           array('\\Apc\\Namespaced\\Foo', 'Apc\\Namespaced\\Foo',   '->loadClass() loads Apc\Namespaced\Foo class'),
+           array('Apc_Pearlike_Foo',    'Apc_Pearlike_Foo',      '->loadClass() loads Apc_Pearlike_Foo class'),
+        );
+    }
+
+    /**
+     * @dataProvider getLoadClassFromFallbackTests
+     */
+    public function testLoadClassFromFallback($className, $testClassName, $message)
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefix('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->addPrefix('Apc_Pearlike_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->addPrefix('', array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/fallback'));
+
+        $loader = new ApcClassLoader('test.prefix.fallback', $loader);
+        $loader->loadClass($testClassName);
+
+        $this->assertTrue(class_exists($className), $message);
+    }
+
+    public function getLoadClassFromFallbackTests()
+    {
+        return array(
+           array('\\Apc\\Namespaced\\Baz',    'Apc\\Namespaced\\Baz',    '->loadClass() loads Apc\Namespaced\Baz class'),
+           array('Apc_Pearlike_Baz',       'Apc_Pearlike_Baz',       '->loadClass() loads Apc_Pearlike_Baz class'),
+           array('\\Apc\\Namespaced\\FooBar', 'Apc\\Namespaced\\FooBar', '->loadClass() loads Apc\Namespaced\Baz class from fallback dir'),
+           array('Apc_Pearlike_FooBar',    'Apc_Pearlike_FooBar',    '->loadClass() loads Apc_Pearlike_Baz class from fallback dir'),
+       );
+    }
+
+    /**
+     * @dataProvider getLoadClassNamespaceCollisionTests
+     */
+    public function testLoadClassNamespaceCollision($namespaces, $className, $message)
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefixes($namespaces);
+
+        $loader = new ApcClassLoader('test.prefix.collision.', $loader);
+        $loader->loadClass($className);
+
+        $this->assertTrue(class_exists($className), $message);
+    }
+
+    public function getLoadClassNamespaceCollisionTests()
+    {
+        return array(
+           array(
+               array(
+                   'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha',
+                   'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta',
+               ),
+               'Apc\NamespaceCollision\A\Foo',
+               '->loadClass() loads NamespaceCollision\A\Foo from alpha.',
+           ),
+           array(
+               array(
+                   'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta',
+                   'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha',
+               ),
+               'Apc\NamespaceCollision\A\Bar',
+               '->loadClass() loads NamespaceCollision\A\Bar from alpha.',
+           ),
+           array(
+               array(
+                   'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha',
+                   'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta',
+               ),
+               'Apc\NamespaceCollision\A\B\Foo',
+               '->loadClass() loads NamespaceCollision\A\B\Foo from beta.',
+           ),
+           array(
+               array(
+                   'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta',
+                   'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha',
+               ),
+               'Apc\NamespaceCollision\A\B\Bar',
+               '->loadClass() loads NamespaceCollision\A\B\Bar from beta.',
+           ),
+        );
+    }
+
+    /**
+     * @dataProvider getLoadClassPrefixCollisionTests
+     */
+    public function testLoadClassPrefixCollision($prefixes, $className, $message)
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefixes($prefixes);
+
+        $loader = new ApcClassLoader('test.prefix.collision.', $loader);
+        $loader->loadClass($className);
+
+        $this->assertTrue(class_exists($className), $message);
+    }
+
+    public function getLoadClassPrefixCollisionTests()
+    {
+        return array(
+           array(
+               array(
+                   'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc',
+                   'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc',
+               ),
+               'ApcPrefixCollision_A_Foo',
+               '->loadClass() loads ApcPrefixCollision_A_Foo from alpha.',
+           ),
+           array(
+               array(
+                   'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc',
+                   'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc',
+               ),
+               'ApcPrefixCollision_A_Bar',
+               '->loadClass() loads ApcPrefixCollision_A_Bar from alpha.',
+           ),
+           array(
+               array(
+                   'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc',
+                   'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc',
+               ),
+               'ApcPrefixCollision_A_B_Foo',
+               '->loadClass() loads ApcPrefixCollision_A_B_Foo from beta.',
+           ),
+           array(
+               array(
+                   'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc',
+                   'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc',
+               ),
+               'ApcPrefixCollision_A_B_Bar',
+               '->loadClass() loads ApcPrefixCollision_A_B_Bar from beta.',
+           ),
+        );
+    }
+}

+ 316 - 0
vendor/symfony/class-loader/Tests/ClassCollectionLoaderTest.php

@@ -0,0 +1,316 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\ClassLoader\ClassCollectionLoader;
+use Symfony\Component\ClassLoader\Tests\Fixtures\DeclaredClass;
+use Symfony\Component\ClassLoader\Tests\Fixtures\WarmedClass;
+
+require_once __DIR__.'/Fixtures/ClassesWithParents/GInterface.php';
+require_once __DIR__.'/Fixtures/ClassesWithParents/CInterface.php';
+require_once __DIR__.'/Fixtures/ClassesWithParents/B.php';
+require_once __DIR__.'/Fixtures/ClassesWithParents/A.php';
+
+class ClassCollectionLoaderTest extends TestCase
+{
+    public function testTraitDependencies()
+    {
+        require_once __DIR__.'/Fixtures/deps/traits.php';
+
+        $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader');
+        $m = $r->getMethod('getOrderedClasses');
+        $m->setAccessible(true);
+
+        $ordered = $m->invoke(null, array('CTFoo'));
+
+        $this->assertEquals(
+            array('TD', 'TC', 'TB', 'TA', 'TZ', 'CTFoo'),
+            array_map(function ($class) { return $class->getName(); }, $ordered)
+        );
+
+        $ordered = $m->invoke(null, array('CTBar'));
+
+        $this->assertEquals(
+            array('TD', 'TZ', 'TC', 'TB', 'TA', 'CTBar'),
+            array_map(function ($class) { return $class->getName(); }, $ordered)
+        );
+    }
+
+    /**
+     * @dataProvider getDifferentOrders
+     */
+    public function testClassReordering(array $classes)
+    {
+        $expected = array(
+            'ClassesWithParents\\GInterface',
+            'ClassesWithParents\\CInterface',
+            'ClassesWithParents\\B',
+            'ClassesWithParents\\A',
+        );
+
+        $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader');
+        $m = $r->getMethod('getOrderedClasses');
+        $m->setAccessible(true);
+
+        $ordered = $m->invoke(null, $classes);
+
+        $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered));
+    }
+
+    public function getDifferentOrders()
+    {
+        return array(
+            array(array(
+                'ClassesWithParents\\A',
+                'ClassesWithParents\\CInterface',
+                'ClassesWithParents\\GInterface',
+                'ClassesWithParents\\B',
+            )),
+            array(array(
+                'ClassesWithParents\\B',
+                'ClassesWithParents\\A',
+                'ClassesWithParents\\CInterface',
+            )),
+            array(array(
+                'ClassesWithParents\\CInterface',
+                'ClassesWithParents\\B',
+                'ClassesWithParents\\A',
+            )),
+            array(array(
+                'ClassesWithParents\\A',
+            )),
+        );
+    }
+
+    /**
+     * @dataProvider getDifferentOrdersForTraits
+     */
+    public function testClassWithTraitsReordering(array $classes)
+    {
+        require_once __DIR__.'/Fixtures/ClassesWithParents/ATrait.php';
+        require_once __DIR__.'/Fixtures/ClassesWithParents/BTrait.php';
+        require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php';
+        require_once __DIR__.'/Fixtures/ClassesWithParents/D.php';
+        require_once __DIR__.'/Fixtures/ClassesWithParents/E.php';
+
+        $expected = array(
+            'ClassesWithParents\\GInterface',
+            'ClassesWithParents\\CInterface',
+            'ClassesWithParents\\ATrait',
+            'ClassesWithParents\\BTrait',
+            'ClassesWithParents\\CTrait',
+            'ClassesWithParents\\B',
+            'ClassesWithParents\\A',
+            'ClassesWithParents\\D',
+            'ClassesWithParents\\E',
+        );
+
+        $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader');
+        $m = $r->getMethod('getOrderedClasses');
+        $m->setAccessible(true);
+
+        $ordered = $m->invoke(null, $classes);
+
+        $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered));
+    }
+
+    public function getDifferentOrdersForTraits()
+    {
+        return array(
+            array(array(
+                'ClassesWithParents\\E',
+                'ClassesWithParents\\ATrait',
+            )),
+            array(array(
+                'ClassesWithParents\\E',
+            )),
+        );
+    }
+
+    public function testFixClassWithTraitsOrdering()
+    {
+        require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php';
+        require_once __DIR__.'/Fixtures/ClassesWithParents/F.php';
+        require_once __DIR__.'/Fixtures/ClassesWithParents/G.php';
+
+        $classes = array(
+            'ClassesWithParents\\F',
+            'ClassesWithParents\\G',
+        );
+
+        $expected = array(
+            'ClassesWithParents\\CTrait',
+            'ClassesWithParents\\F',
+            'ClassesWithParents\\G',
+        );
+
+        $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader');
+        $m = $r->getMethod('getOrderedClasses');
+        $m->setAccessible(true);
+
+        $ordered = $m->invoke(null, $classes);
+
+        $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered));
+    }
+
+    /**
+     * @dataProvider getFixNamespaceDeclarationsData
+     */
+    public function testFixNamespaceDeclarations($source, $expected)
+    {
+        $this->assertEquals('<?php '.$expected, ClassCollectionLoader::fixNamespaceDeclarations('<?php '.$source));
+    }
+
+    public function getFixNamespaceDeclarationsData()
+    {
+        return array(
+            array("namespace;\nclass Foo {}\n", "namespace\n{\nclass Foo {}\n}"),
+            array("namespace Foo;\nclass Foo {}\n", "namespace Foo\n{\nclass Foo {}\n}"),
+            array("namespace   Bar ;\nclass Foo {}\n", "namespace Bar\n{\nclass Foo {}\n}"),
+            array("namespace Foo\Bar;\nclass Foo {}\n", "namespace Foo\Bar\n{\nclass Foo {}\n}"),
+            array("namespace Foo\Bar\Bar\n{\nclass Foo {}\n}\n", "namespace Foo\Bar\Bar\n{\nclass Foo {}\n}"),
+            array("namespace\n{\nclass Foo {}\n}\n", "namespace\n{\nclass Foo {}\n}"),
+        );
+    }
+
+    /**
+     * @dataProvider getFixNamespaceDeclarationsDataWithoutTokenizer
+     */
+    public function testFixNamespaceDeclarationsWithoutTokenizer($source, $expected)
+    {
+        ClassCollectionLoader::enableTokenizer(false);
+        $this->assertEquals('<?php '.$expected, ClassCollectionLoader::fixNamespaceDeclarations('<?php '.$source));
+        ClassCollectionLoader::enableTokenizer(true);
+    }
+
+    public function getFixNamespaceDeclarationsDataWithoutTokenizer()
+    {
+        return array(
+            array("namespace;\nclass Foo {}\n", "namespace\n{\nclass Foo {}\n}\n"),
+            array("namespace Foo;\nclass Foo {}\n", "namespace Foo\n{\nclass Foo {}\n}\n"),
+            array("namespace   Bar ;\nclass Foo {}\n", "namespace   Bar\n{\nclass Foo {}\n}\n"),
+            array("namespace Foo\Bar;\nclass Foo {}\n", "namespace Foo\Bar\n{\nclass Foo {}\n}\n"),
+            array("namespace Foo\Bar\Bar\n{\nclass Foo {}\n}\n", "namespace Foo\Bar\Bar\n{\nclass Foo {}\n}\n"),
+            array("\nnamespace\n{\nclass Foo {}\n\$namespace=123;}\n", "\nnamespace\n{\nclass Foo {}\n\$namespace=123;}\n"),
+        );
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testUnableToLoadClassException()
+    {
+        if (is_file($file = sys_get_temp_dir().'/foo.php')) {
+            unlink($file);
+        }
+
+        ClassCollectionLoader::load(array('SomeNotExistingClass'), sys_get_temp_dir(), 'foo', false);
+    }
+
+    public function testCommentStripping()
+    {
+        if (is_file($file = __DIR__.'/bar.php')) {
+            unlink($file);
+        }
+        spl_autoload_register($r = function ($class) {
+            if (0 === strpos($class, 'Namespaced') || 0 === strpos($class, 'Pearlike_')) {
+                @require_once __DIR__.'/Fixtures/'.str_replace(array('\\', '_'), '/', $class).'.php';
+            }
+        });
+
+        $strictTypes = defined('HHVM_VERSION') ? '' : "\nnamespace {require __DIR__.'/Fixtures/Namespaced/WithStrictTypes.php';}";
+
+        ClassCollectionLoader::load(
+            array('Namespaced\\WithComments', 'Pearlike_WithComments', 'Namespaced\\WithDirMagic', 'Namespaced\\WithFileMagic', 'Namespaced\\WithHaltCompiler', $strictTypes ? 'Namespaced\\WithStrictTypes' : 'Namespaced\\WithComments'),
+            __DIR__,
+            'bar',
+            false
+        );
+
+        spl_autoload_unregister($r);
+
+        $this->assertEquals(<<<'EOF'
+namespace Namespaced
+{
+class WithComments
+{
+public static $loaded = true;
+}
+$string ='string should not be   modified {$string}';
+$heredoc = (<<<HD
+
+
+Heredoc should not be   modified {$string}
+
+
+HD
+);
+$nowdoc =<<<'ND'
+
+
+Nowdoc should not be   modified {$string}
+
+
+ND
+;
+}
+namespace
+{
+class Pearlike_WithComments
+{
+public static $loaded = true;
+}
+}
+namespace {require __DIR__.'/Fixtures/Namespaced/WithDirMagic.php';}
+namespace {require __DIR__.'/Fixtures/Namespaced/WithFileMagic.php';}
+namespace {require __DIR__.'/Fixtures/Namespaced/WithHaltCompiler.php';}
+EOF
+            .$strictTypes,
+            str_replace(array("<?php \n", '\\\\'), array('', '/'), file_get_contents($file))
+        );
+
+        unlink($file);
+    }
+
+    public function testInline()
+    {
+        $this->assertTrue(class_exists(WarmedClass::class, true));
+
+        @unlink($cache = sys_get_temp_dir().'/inline.php');
+
+        $classes = array(WarmedClass::class);
+        $excluded = array(DeclaredClass::class);
+
+        ClassCollectionLoader::inline($classes, $cache, $excluded);
+
+        $this->assertSame(<<<'EOTXT'
+<?php 
+namespace Symfony\Component\ClassLoader\Tests\Fixtures
+{
+interface WarmedInterface
+{
+}
+}
+namespace Symfony\Component\ClassLoader\Tests\Fixtures
+{
+class WarmedClass extends DeclaredClass implements WarmedInterface
+{
+}
+}
+EOTXT
+            , file_get_contents($cache)
+        );
+
+        unlink($cache);
+    }
+}

+ 235 - 0
vendor/symfony/class-loader/Tests/ClassLoaderTest.php

@@ -0,0 +1,235 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\ClassLoader\ClassLoader;
+
+class ClassLoaderTest extends TestCase
+{
+    public function testGetPrefixes()
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->addPrefix('Bar', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->addPrefix('Bas', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $prefixes = $loader->getPrefixes();
+        $this->assertArrayHasKey('Foo', $prefixes);
+        $this->assertArrayNotHasKey('Foo1', $prefixes);
+        $this->assertArrayHasKey('Bar', $prefixes);
+        $this->assertArrayHasKey('Bas', $prefixes);
+    }
+
+    public function testGetFallbackDirs()
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefix(null, __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->addPrefix(null, __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $fallback_dirs = $loader->getFallbackDirs();
+        $this->assertCount(2, $fallback_dirs);
+    }
+
+    /**
+     * @dataProvider getLoadClassTests
+     */
+    public function testLoadClass($className, $testClassName, $message)
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefix('Namespaced2\\', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->addPrefix('Pearlike2_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->loadClass($testClassName);
+        $this->assertTrue(class_exists($className), $message);
+    }
+
+    public function getLoadClassTests()
+    {
+        return array(
+            array('\\Namespaced2\\Foo', 'Namespaced2\\Foo',   '->loadClass() loads Namespaced2\Foo class'),
+            array('\\Pearlike2_Foo',    'Pearlike2_Foo',      '->loadClass() loads Pearlike2_Foo class'),
+        );
+    }
+
+    /**
+     * @dataProvider getLoadNonexistentClassTests
+     */
+    public function testLoadNonexistentClass($className, $testClassName, $message)
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefix('Namespaced2\\', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->addPrefix('Pearlike2_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->loadClass($testClassName);
+        $this->assertFalse(class_exists($className), $message);
+    }
+
+    public function getLoadNonexistentClassTests()
+    {
+        return array(
+            array('\\Pearlike3_Bar', '\\Pearlike3_Bar', '->loadClass() loads non existing Pearlike3_Bar class with a leading slash'),
+        );
+    }
+
+    public function testAddPrefixSingle()
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $prefixes = $loader->getPrefixes();
+        $this->assertArrayHasKey('Foo', $prefixes);
+        $this->assertCount(1, $prefixes['Foo']);
+    }
+
+    public function testAddPrefixesSingle()
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefixes(array('Foo' => array('foo', 'foo')));
+        $loader->addPrefixes(array('Foo' => array('foo')));
+        $prefixes = $loader->getPrefixes();
+        $this->assertArrayHasKey('Foo', $prefixes);
+        $this->assertCount(1, $prefixes['Foo'], print_r($prefixes, true));
+    }
+
+    public function testAddPrefixMulti()
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefix('Foo', 'foo');
+        $loader->addPrefix('Foo', 'bar');
+        $prefixes = $loader->getPrefixes();
+        $this->assertArrayHasKey('Foo', $prefixes);
+        $this->assertCount(2, $prefixes['Foo']);
+        $this->assertContains('foo', $prefixes['Foo']);
+        $this->assertContains('bar', $prefixes['Foo']);
+    }
+
+    public function testUseIncludePath()
+    {
+        $loader = new ClassLoader();
+        $this->assertFalse($loader->getUseIncludePath());
+
+        $this->assertNull($loader->findFile('Foo'));
+
+        $includePath = get_include_path();
+
+        $loader->setUseIncludePath(true);
+        $this->assertTrue($loader->getUseIncludePath());
+
+        set_include_path(__DIR__.'/Fixtures/includepath'.PATH_SEPARATOR.$includePath);
+
+        $this->assertEquals(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'includepath'.DIRECTORY_SEPARATOR.'Foo.php', $loader->findFile('Foo'));
+
+        set_include_path($includePath);
+    }
+
+    /**
+     * @dataProvider getLoadClassFromFallbackTests
+     */
+    public function testLoadClassFromFallback($className, $testClassName, $message)
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefix('Namespaced2\\', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->addPrefix('Pearlike2_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
+        $loader->addPrefix('', array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/fallback'));
+        $loader->loadClass($testClassName);
+        $this->assertTrue(class_exists($className), $message);
+    }
+
+    public function getLoadClassFromFallbackTests()
+    {
+        return array(
+            array('\\Namespaced2\\Baz',    'Namespaced2\\Baz',    '->loadClass() loads Namespaced2\Baz class'),
+            array('\\Pearlike2_Baz',       'Pearlike2_Baz',       '->loadClass() loads Pearlike2_Baz class'),
+            array('\\Namespaced2\\FooBar', 'Namespaced2\\FooBar', '->loadClass() loads Namespaced2\Baz class from fallback dir'),
+            array('\\Pearlike2_FooBar',    'Pearlike2_FooBar',    '->loadClass() loads Pearlike2_Baz class from fallback dir'),
+        );
+    }
+
+    /**
+     * @dataProvider getLoadClassNamespaceCollisionTests
+     */
+    public function testLoadClassNamespaceCollision($namespaces, $className, $message)
+    {
+        $loader = new ClassLoader();
+        $loader->addPrefixes($namespaces);
+
+        $loader->loadClass($className);
+        $this->assertTrue(class_exists($className), $message);
+    }
+
+    public function getLoadClassNamespaceCollisionTests()
+    {
+        return array(
+            array(
+                array(
+                    'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha',
+                    'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta',
+                ),
+                'NamespaceCollision\C\Foo',
+                '->loadClass() loads NamespaceCollision\C\Foo from alpha.',
+            ),
+            array(
+                array(
+                    'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta',
+                    'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha',
+                ),
+                'NamespaceCollision\C\Bar',
+                '->loadClass() loads NamespaceCollision\C\Bar from alpha.',
+            ),
+            array(
+                array(
+                    'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha',
+                    'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta',
+                ),
+                'NamespaceCollision\C\B\Foo',
+                '->loadClass() loads NamespaceCollision\C\B\Foo from beta.',
+            ),
+            array(
+                array(
+                    'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta',
+                    'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha',
+                ),
+                'NamespaceCollision\C\B\Bar',
+                '->loadClass() loads NamespaceCollision\C\B\Bar from beta.',
+            ),
+            array(
+                array(
+                    'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha',
+                    'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta',
+                ),
+                'PrefixCollision_C_Foo',
+                '->loadClass() loads PrefixCollision_C_Foo from alpha.',
+            ),
+            array(
+                array(
+                    'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta',
+                    'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha',
+                ),
+                'PrefixCollision_C_Bar',
+                '->loadClass() loads PrefixCollision_C_Bar from alpha.',
+            ),
+            array(
+                array(
+                    'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha',
+                    'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta',
+                ),
+                'PrefixCollision_C_B_Foo',
+                '->loadClass() loads PrefixCollision_C_B_Foo from beta.',
+            ),
+            array(
+                array(
+                    'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta',
+                    'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha',
+                ),
+                'PrefixCollision_C_B_Bar',
+                '->loadClass() loads PrefixCollision_C_B_Bar from beta.',
+            ),
+        );
+    }
+}

+ 148 - 0
vendor/symfony/class-loader/Tests/ClassMapGeneratorTest.php

@@ -0,0 +1,148 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ClassLoader\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\ClassLoader\ClassMapGenerator;
+
+class ClassMapGeneratorTest extends TestCase
+{
+    /**
+     * @var string|null
+     */
+    private $workspace = null;
+
+    public function prepare_workspace()
+    {
+        $this->workspace = sys_get_temp_dir().'/'.microtime(true).'.'.mt_rand();
+        mkdir($this->workspace, 0777, true);
+        $this->workspace = realpath($this->workspace);
+    }
+
+    /**
+     * @param string $file
+     */
+    private function clean($file)
+    {
+        if (is_dir($file) && !is_link($file)) {
+            $dir = new \FilesystemIterator($file);
+            foreach ($dir as $childFile) {
+                $this->clean($childFile);
+            }
+
+            rmdir($file);
+        } else {
+            unlink($file);
+        }
+    }
+
+    /**
+     * @dataProvider getTestCreateMapTests
+     */
+    public function testDump($directory)
+    {
+        $this->prepare_workspace();
+
+        $file = $this->workspace.'/file';
+
+        $generator = new ClassMapGenerator();
+        $generator->dump($directory, $file);
+        $this->assertFileExists($file);
+
+        $this->clean($this->workspace);
+    }
+
+    /**
+     * @dataProvider getTestCreateMapTests
+     */
+    public function testCreateMap($directory, $expected)
+    {
+        $this->assertEqualsNormalized($expected, ClassMapGenerator::createMap($directory));
+    }
+
+    public function getTestCreateMapTests()
+    {
+        $data = array(
+            array(__DIR__.'/Fixtures/Namespaced', array(
+                'Namespaced\\Bar' => realpath(__DIR__).'/Fixtures/Namespaced/Bar.php',
+                'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php',
+                'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php',
+                'Namespaced\\WithComments' => realpath(__DIR__).'/Fixtures/Namespaced/WithComments.php',
+                'Namespaced\\WithStrictTypes' => realpath(__DIR__).'/Fixtures/Namespaced/WithStrictTypes.php',
+                'Namespaced\\WithHaltCompiler' => realpath(__DIR__).'/Fixtures/Namespaced/WithHaltCompiler.php',
+                'Namespaced\\WithDirMagic' => realpath(__DIR__).'/Fixtures/Namespaced/WithDirMagic.php',
+                'Namespaced\\WithFileMagic' => realpath(__DIR__).'/Fixtures/Namespaced/WithFileMagic.php',
+            )),
+            array(__DIR__.'/Fixtures/beta/NamespaceCollision', array(
+                'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php',
+                'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php',
+                'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php',
+                'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php',
+            )),
+            array(__DIR__.'/Fixtures/Pearlike', array(
+                'Pearlike_Foo' => realpath(__DIR__).'/Fixtures/Pearlike/Foo.php',
+                'Pearlike_Bar' => realpath(__DIR__).'/Fixtures/Pearlike/Bar.php',
+                'Pearlike_Baz' => realpath(__DIR__).'/Fixtures/Pearlike/Baz.php',
+                'Pearlike_WithComments' => realpath(__DIR__).'/Fixtures/Pearlike/WithComments.php',
+            )),
+            array(__DIR__.'/Fixtures/classmap', array(
+                'Foo\\Bar\\A' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php',
+                'Foo\\Bar\\B' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php',
+                'A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
+                'Alpha\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
+                'Alpha\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
+                'Beta\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
+                'Beta\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
+                'ClassMap\\SomeInterface' => realpath(__DIR__).'/Fixtures/classmap/SomeInterface.php',
+                'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php',
+                'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php',
+            )),
+            array(__DIR__.'/Fixtures/php5.4', array(
+                'TFoo' => __DIR__.'/Fixtures/php5.4/traits.php',
+                'CFoo' => __DIR__.'/Fixtures/php5.4/traits.php',
+                'Foo\\TBar' => __DIR__.'/Fixtures/php5.4/traits.php',
+                'Foo\\IBar' => __DIR__.'/Fixtures/php5.4/traits.php',
+                'Foo\\TFooBar' => __DIR__.'/Fixtures/php5.4/traits.php',
+                'Foo\\CBar' => __DIR__.'/Fixtures/php5.4/traits.php',
+            )),
+            array(__DIR__.'/Fixtures/php5.5', array(
+                'ClassCons\\Foo' => __DIR__.'/Fixtures/php5.5/class_cons.php',
+            )),
+        );
+
+        return $data;
+    }
+
+    public function testCreateMapFinderSupport()
+    {
+        $finder = new \Symfony\Component\Finder\Finder();
+        $finder->files()->in(__DIR__.'/Fixtures/beta/NamespaceCollision');
+
+        $this->assertEqualsNormalized(array(
+            'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php',
+            'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php',
+            'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php',
+            'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php',
+        ), ClassMapGenerator::createMap($finder));
+    }
+
+    protected function assertEqualsNormalized($expected, $actual, $message = null)
+    {
+        foreach ($expected as $ns => $path) {
+            $expected[$ns] = str_replace('\\', '/', $path);
+        }
+        foreach ($actual as $ns => $path) {
+            $actual[$ns] = str_replace('\\', '/', $path);
+        }
+        $this->assertEquals($expected, $actual, $message);
+    }
+}

+ 17 - 0
vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Bar.php

@@ -0,0 +1,17 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Apc\Namespaced;
+
+class Bar
+{
+    public static $loaded = true;
+}

+ 17 - 0
vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Baz.php

@@ -0,0 +1,17 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Apc\Namespaced;
+
+class Baz
+{
+    public static $loaded = true;
+}

+ 17 - 0
vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Foo.php

@@ -0,0 +1,17 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Apc\Namespaced;
+
+class Foo
+{
+    public static $loaded = true;
+}

+ 17 - 0
vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/FooBar.php

@@ -0,0 +1,17 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Apc\Namespaced;
+
+class FooBar
+{
+    public static $loaded = true;
+}

+ 6 - 0
vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Bar.php

@@ -0,0 +1,6 @@
+<?php
+
+class Apc_Pearlike_Bar
+{
+    public static $loaded = true;
+}

+ 6 - 0
vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Baz.php

@@ -0,0 +1,6 @@
+<?php
+
+class Apc_Pearlike_Baz
+{
+    public static $loaded = true;
+}

+ 6 - 0
vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Foo.php

@@ -0,0 +1,6 @@
+<?php
+
+class Apc_Pearlike_Foo
+{
+    public static $loaded = true;
+}

+ 6 - 0
vendor/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/ApcPrefixCollision/A/Bar.php

@@ -0,0 +1,6 @@
+<?php
+
+class ApcPrefixCollision_A_Bar
+{
+    public static $loaded = true;
+}

Some files were not shown because too many files changed in this diff